DRT modified file to work on any computer

2022-01-17 Some files appeared to be missing to run this notebook. Trying to get all to run

Overview

This is a pipeline for differential analysis of RNASeq data from SKMEL5 sublines using DESeq2 statistical package. Three sublines: SC01 (regressing), SC07 (stationary) and SC10 (expanding) were analyzed for gene expression differences. In addition, time course changes in 8uM PLX4720 were also performed for each subline. Time points are: 0, 3d, 8d. The differential analysis will be performed based on the contrasts defined below. General steps for the analysis are:

###1. Read counts table: + Could be read directly as a csv/txt file. + Alignment and read counts could be done within R environment to create read counts table. 1. Define working directory, load the required libraries. 2. Get read counts table. Read the raw counts file processed by featureCounts. The fastq files were aligned with HiSat2, and the read counts were obtained using featureCounts of Rsubread packages.

pkgs <- c("BiocManager","DESeq2","org.Hs.eg.db","clusterProfiler","HDO.db",
          "pheatmap","ggnewscale","PoiClaClu","enrichR","gtable","Rmisc")
source("getReqdPkgs.r")
getReqdPkgs(pkgs)
suppressPackageStartupMessages(expr={
    library(plyr)
    library(dplyr)
    library(ggplot2)
    library(ggnewscale)
    library(reshape2)
    library(DESeq2)
    library(ggrepel)
    library(pheatmap)
    library(org.Hs.eg.db)
    library(clusterProfiler)
    library("RColorBrewer")
    library(enrichR)
    library(biomaRt)
    library(Rmisc)
})

SAVEFILES <- FALSE
d <- read.csv("../data/featureCounts_matrix_all.csv", header=T, sep=",")

#Rename columns
cols <- c("ensembl_gene_id", "SC01_day0_rep1", "SC01_day0_rep2", "SC01_day0_rep3",
          "SC01_day3_rep1", "SC01_day3_rep2", "SC01_day3_rep3",
          "SC01_day8_rep1", "SC01_day8_rep2", "SC01_day8_rep3",
          "SC07_day0_rep1", "SC07_day0_rep2", "SC07_day0_rep3",
          "SC07_day3_rep1", "SC07_day3_rep2", "SC07_day3_rep3",
          "SC07_day8_rep1", "SC07_day8_rep2", "SC07_day8_rep3",
          "SC10_day0_rep1", "SC10_day0_rep2", "SC10_day0_rep3",
          "SC10_day3_rep1", "SC10_day3_rep2", "SC10_day3_rep3",
          "SC10_day8_rep1", "SC10_day8_rep2", "SC10_day8_rep3")
names(d) <- cols
ensembl <- useEnsembl("ensembl", mirror="useast")
mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
genes <- d$ensembl_gene_id
G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
                filters= "ensembl_gene_id",
                values=genes,
                mart=mart)

GE_data <- merge(d, G_list, by = "ensembl_gene_id")
d <- GE_data[, -1]
d <- d[c(28, seq(1:27))]
rownames(d) <- make.names(d$hgnc_symbol, unique = T)
d <- d[, 2:28]

# remove genes with <5 counts in all samples
d <- d[apply(d, 1, function(x) all(x > 5)),]


countdata <- d
# baseline <- c(1,2,3,10,11,12,19,20,21)
# treat3d  <- c(4,5,6,13,14,15,22,23,24)
# treat8d  <- c(7,8,9,16,17,18,25,26,27)
# # define the groups by subclones
# sc01 <- c(baseline[1:3], treat3d[1:3], treat8d[1:3])
# sc07 <- c(baseline[4:6], treat3d[4:6], treat8d[4:6])
# sc10 <- c(baseline[7:9], treat3d[7:9], treat8d[7:9])
# # Get the countdata specific to conditions: 
# # countdata <- countdata[,c(baseline)] 
# rownames(countdata) <- d[,"ensembl_gene_id"]
head(countdata)

Identifying different ion channel gene lists

###2. Convert counts table to DESeq2 object. Convert counts table to object for DESeq2 or any other analysis pipeline. This step will require to prepare data object in a form that is suitable for analysis in DESeq2 pipeline: we will need the following to proceed:

  • countdata: a table with the read/fragment counts.
  • coldata: a table with information about the samples.

Using the matrix of counts and the sample information table, we need to construct the DESeqDataSet object, for which we will use DESeqDataSetFromMatrix…..

1. Define the samples and treatment conditions.

condition <- c("0", "3", "8")
treatment <- rep(condition, each=3) # Three biological replicates
unique(treatment)
[1] "0" "3" "8"
cell <- c("SC01", "SC07","SC10") #sublines used for the analysis
cellName <- rep(cell, each=3)

coldata <- data.frame(cell=rep(cellName), treatment=rep(treatment, each=3))
group = factor(paste(coldata$cell, coldata$treatment, sep="."))
coldata$group = group

2. construct the DESeqDataSet object from the matrix of counts and the sample information table.

Described above are: countdata- raw counts, coldata: sample information table.

dds <- DESeqDataSetFromMatrix(countData = countdata,
                              colData = coldata,
                              design = ~ cell + treatment + cell:treatment)
Warning: some variables in design formula are characters, converting to factors
dds
class: DESeqDataSet 
dim: 14944 27 
metadata(1): version
assays(1): counts
rownames(14944): TSPAN6 DPM1 ... GTF2IP12 X.16468
rowData names(0):
colnames(27): SC01_day0_rep1 SC01_day0_rep2 ... SC10_day8_rep2 SC10_day8_rep3
colData names(3): cell treatment group

###3. Exploratory analysis and visualization. There are two separate steps in the workflow; the one which involves data transformations in order to visualize sample relationships and the second step involves statistical testing methods which requires the original raw counts.

1. Pre-filtering and normalization.

Pre-filtering and normalization is required to remove lowly expressed genes.

dds2 <- dds[rowSums(counts(dds)) > 18, ] # remove rows with minimum of 2 read per condition

nrow(dds2)
[1] 14944
# save(dds2, file = "DDS_SC-1,7,10_cell-treat-int.RData")
# load("DDS_SC-1,7,10_cell-treat-int.RData")

2. Visualize sample-to-sample distances.

We could use Principal Component Analysis (PCA) to visualize relationships between samples.

rld <- rlog(dds2, blind = FALSE)
# save(rld, file = "RLD_SC-1,7,10_0,3,8d_20180701.RData")
# load("RLD_SC-1,7,10_0,3,8d_20180701.RData")
plotPCA(rld, intgroup = c("cell", "treatment"), ntop=5000)
using ntop=5000 top features by variance

## Use prcomp function
# Colored by cell line, shape by time point, lines connecting time
pca_DEseq <- prcomp(t(assay(rld)))
pca_DEseq_perc <- round(100*pca_DEseq$sdev^2/sum(pca_DEseq$sdev^2),1)
pca_DEseq_df <- data.frame(PC1 = pca_DEseq$x[,1], 
                           PC2 = pca_DEseq$x[,2], 
                           sample = colnames(assay(rld)),
                           cell.line = rep(c("SC01", "SC07", "SC10"), each = 9),
                           day = rep(c("Day0", "Day3", "Day8"), each = 3),
                           replicate = rep(c("Rep1", "Rep2", "Rep3"), times=9))

pca_DEseq_means <- ddply(pca_DEseq_df, .(cell.line, day), summarise, meanPC1 = mean(PC1), meanPC2 = mean(PC2))

ggplot(pca_DEseq_df, aes(PC1,PC2, color = cell.line))+
  geom_point(aes(shape = day), size=5) +
  geom_path(data = pca_DEseq_means, 
            aes(x=meanPC1, y=meanPC2,
                color=cell.line), arrow = arrow(),
            size = 2) +
  labs(x=paste0("PC1 (",pca_DEseq_perc[1],"% variance)"), y=paste0("PC2 (",pca_DEseq_perc[2],"% variance)")) +
  theme_bw() + ggtitle("PCA - Subclones in Time") +
  theme(legend.text = element_text(size = 12), 
        plot.title = element_text(size = 14, 
                                  hjust = 0.5, 
                                  face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"),
        legend.position = "bottom",
        axis.title=element_text(size=12, face="bold"))
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

NA
NA
NA

4. Differential Expression Analysis.

Always make sure to use the unnormalized raw counts for this. We will use DESeq function to perform differential analysis between samples; Unless specified, the analysis is between the last group and the first group. Different comparison can be done using ‘contrast’ argument. Steps involved underneath:

  1. estimation of size factors (controls for differences in sequencing depth of the samples)
  2. estimation of dispersion values for each gene,
  3. fitting a generalized linear model

1. Running the differential expression pipeline.

design(dds2) = ~ cell + treatment + cell:treatment
dds <- DESeq(dds2, test = "LRT", reduced = ~ cell + treatment)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
# save(dds, file = "DESeq_SC1,7,10_Timecourse_LRT.RData")
# load("DESeq_SC1,7,10_Timecourse_LRT.RData")
# dds

2. Building the results table.

By default, results will extract the estimated log2 fold changes and p values for the last variable in the design formula. If there are more than 2 levels for this variable, results will extract the results table for a comparison of the last level over the first level.

# Esimate the differences between groups by: # a) Lowering the FDR (padj) or (b) raise the log2 fold change.

resultsNames(dds)
[1] "Intercept"           "cell_SC07_vs_SC01"   "cell_SC10_vs_SC01"   "treatment_3_vs_0"    "treatment_8_vs_0"   
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
# alpha = FDR adjusted p value cutoff
res <- results(dds, alpha = 0.001)
summary(res)

out of 14944 with nonzero total read count
adjusted p-value < 0.001
LFC > 0 (up)       : 3918, 26%
LFC < 0 (down)     : 4377, 29%
outliers [1]       : 2, 0.013%
low counts [2]     : 290, 1.9%
(mean count < 19)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
resOrdered <- res[order(res$pvalue),]
rdata = as.data.frame(res)

Differential expression: days 0 to 8

Change significant log2 fold change to 1.585 (== 3-fold change in log2 space).

res_0to8d <- results(dds, name="treatment_8_vs_0", cooksCutoff = 0.99, 
                     independentFiltering = TRUE, alpha = 0.05, pAdjustMethod = "BH")
summary(res_0to8d)

out of 14944 with nonzero total read count
adjusted p-value < 0.05
LFC > 0 (up)       : 5580, 37%
LFC < 0 (down)     : 5275, 35%
outliers [1]       : 23, 0.15%
low counts [2]     : 0, 0%
(mean count < 10)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# order results table by the smallest adjusted p value:
res_0to8d <- res_0to8d[order(res_0to8d$padj),]
results_0to8d <- as.data.frame(res_0to8d)

results_0to8d <- mutate(results_0to8d, sig=ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange > 1.585, "Upregulated", ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange < -1.585, "Downregulated", "Not Significant")))

row.names(results_0to8d) <- row.names(res_0to8d)


head(results_0to8d)
DEgenes_0to8d <- results_0to8d[which(abs(results_0to8d$log2FoldChange) > log2(1.5) & results_0to8d$padj < 0.05),]

if(SAVEFILES) write.csv(DEgenes_0to8d, file="~/Desktop/DEgenes_0to8d.csv")

Volcano plot

volcano <- ggplot(results_0to8d, aes(log2FoldChange, -log10(pvalue))) +
  geom_point(aes(col = sig)) + theme_bw() +
  scale_color_manual(values = c("red", "grey", "green3")) +
  # ggtitle("Volcano Plot of Untreated vs Idling") +
  labs(x="log2(Fold Change)", y="Log(Odds Ratio)") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12), 
        axis.title=element_text(size=12),
        legend.position = "none")

volcano

# volcano + ggrepel::geom_text_repel(data=results_0to8d[1:10, ], 
#                                    ggplot2::aes(label=rownames(results_0to8d[1:10, ])))
# save(results_0to8d, file="untreatedIdling_DEA.RData")
DEgenes_0to8d <- DEgenes_0to8d[order(abs(DEgenes_0to8d$log2FoldChange),DEgenes_0to8d$sig, decreasing = TRUE),]
temp <- DEgenes_0to8d[DEgenes_0to8d$baseMean > 300,]
temp <- temp[abs(temp$log2FoldChange)>2,]
if(SAVEFILES) write.csv(DEgenes_0to8d, file = "DEgenes_0to8d.csv")

Generating Ion Channel Specific Gene Dataframes

test <- assay(dds)
types <- c("ATP", "TRP", "GABR", "CRACR", "SLC", "KCN", "CACN", "GRI", "ABC", "SCN", "TRP", "RIC3", "CHRND", "RYR")
samples <- c("SC01_day0", "SC01_day3", "SC01_day8", "SC07_day0", "SC07_day3", "SC07_day8", "SC10_day0", "SC10_day3", "SC10_day8")
test <- test[grep(paste(types, collapse="|"), rownames(test)),]
test1 <- sapply(samples, function(x) rowMeans(test[, grep(x, colnames(test))]))
rownames(test1) <- rownames(test)
test1 <- test1[order(rownames(test1)),]
test2 <- as.data.frame(test1)
test2["l2FC_SC01_0to8"] <- log2(test2["SC01_day8"]/test2["SC01_day0"])
test2["l2FC_SC07_0to8"] <- log2(test2["SC07_day8"]/test2["SC07_day0"])
test2["l2FC_SC10_0to8"] <- log2(test2["SC10_day8"]/test2["SC10_day0"])
test3 <- subset(test2, l2FC_SC01_0to8 > 1 & l2FC_SC07_0to8 > 1 & l2FC_SC10_0to8 > 1)
test4 <- subset(test2, l2FC_SC01_0to8 > 1 | l2FC_SC07_0to8 > 1 | l2FC_SC10_0to8 > 1)
# write.csv(x = test2, file = "all_ionChannel_Expression.csv")
# write.csv(x = test3, file = "allUpreg_ionChannel_Expression.csv")
# write.csv(x = test4, file = "atLeastOneUpreg_ionChannel_Expression.csv")
test5 <- log2(test4[, 1:9]+1)
pheatmap(test5, cluster_cols = F, cluster_rows = F)

test6 <- log2((test3[,1:9])+1)
pheatmap(test6, cluster_rows = F, cluster_cols = F)

test7 <- test5[rowSums(test5)>30,]
pheatmap(test7, cluster_rows = F, cluster_cols = F)

# load(file="untreatedIdling_DEA.RData")

OrgDB <- org.Hs.eg.db
upreg_genes <- subset(results_0to8d, padj<0.05 & log2FoldChange>2)
downreg_genes <-subset(results_0to8d, padj<0.05 & log2FoldChange<(-2))

geneList_up <- as.vector(upreg_genes$log2FoldChange)
names(geneList_up) <- rownames(upreg_genes)
geneList_down <- as.vector(downreg_genes$log2FoldChange)
names(geneList_down) <- rownames(downreg_genes)

genes_up <- as.vector(rownames(upreg_genes))
genes_down <- as.vector(rownames(downreg_genes))
# names(geneList) <- rownames(results_0to8d)
genes_up_ENTREZID <- bitr(genes_up, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
genes_down_ENTREZID <- bitr(genes_down, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID

# Group GO
ggo_up <- clusterProfiler::groupGO(gene     = genes_up_ENTREZID,
                                OrgDb    = OrgDB,
                                ont      = "BP",
                                level    = 3,
                                readable = TRUE)
ggo_up_df <- as.data.frame(ggo_up)
ggo_up_df <- ggo_up_df[order(-ggo_up_df$Count),] 

ggo_down <- clusterProfiler::groupGO(gene = genes_down_ENTREZID,
                                OrgDb    = OrgDB,
                                ont      = "BP",
                                level    = 3,
                                readable = TRUE)
# View(as.data.frame(ggo_down))

# GO over-representation test
ego_genesUp <- clusterProfiler::enrichGO(gene  = genes_up_ENTREZID,
                                 OrgDb         = OrgDB,
                                 ont           = "BP",
                                 pAdjustMethod = "BH",
                                 pvalueCutoff  = 0.05,
                                 qvalueCutoff  = 0.05, 
                                 readable      = TRUE)

# View(as.data.frame(ego_genesUp))

ego_genesDown <- clusterProfiler::enrichGO(gene  = genes_down_ENTREZID,
                                 OrgDb         = OrgDB,
                                 ont           = "BP",
                                 pAdjustMethod = "BH",
                                 pvalueCutoff  = 0.05,
                                 qvalueCutoff  = 0.05, 
                                 readable      = TRUE)

# View(as.data.frame(ego_genesDown))

# kk_genesUp <- enrichKEGG(gene = genes_up_ENTREZID,
#                    organism = 'hsa',
#                   pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesUp))
# 
# kk_genesDown <- enrichKEGG(gene = genes_down_ENTREZID,
#                    organism = 'hsa',
#                   pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesDown))

# ego_GSEA_up <- gseGO(geneList = geneList_up,
#               OrgDb        = OrgDB,
#               ont          = "BP",
#               nPerm        = 1000,
#               minGSSize    = 100,
#               maxGSSize    = 500,
#               pvalueCutoff = 0.05,
#               verbose      = FALSE)

# barplot(ggo_up, order=T)
# barplot(ggo_down)
enrichplot::dotplot(ego_genesUp) + ggtitle("GO Over-representation Upregulated Genes") +
  labs(x="Gene Ratio", y="GO Terms") +
  theme(legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"), 
        axis.title=element_text(size=12, face="bold"))


enrichplot::dotplot(ego_genesDown) + ggtitle("GO Over-representation Downregulated Genes") +
  labs(x="Gene Ratio", y="GO Terms") +
  theme(legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"), 
        axis.title=element_text(size=12, face="bold"))


# emapplot(ego_genesUp)
# emapplot(ego_genesDown)
enrichplot::cnetplot(ego_genesUp, categorySize="pvalue", color.params = list(foldChange = geneList_up))
Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

enrichplot::cnetplot(ego_genesDown, categorySize="pvalue", color.params = list(foldChange = geneList_down))
Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

ego_genesUp_df <- as.data.frame(ego_genesUp) 
egoUp <- ego_genesUp_df[order(-ego_genesUp_df$Count),]
# sorted_egoUp_top10 <- head(egoUp, 10)
egoUp_genes <- strsplit(egoUp$geneID, "/", fixed=TRUE)
# egoUp_top10_genes_all <- unlist(strsplit(head(egoUp, 10)$geneID, "[/]"))
# egoUp_top10_genes_group <- strsplit(sorted_egoUp_top10$geneID, "[/]")
# egoUp_top10_genes_unique <- unique(egoUp_top10_genes)
# table(egoUp_top10_genes)
# egoUp_genesByGroup <- as.data.frame(t(plyr::ldply(egoUp_top10_genes_group, rbind)))
# colnames(egoUp_genesByGroup) <- sorted_egoUp_top10$Description
# egoUp_genesByGroup_ionOnly <- egoUp_genesByGroup[,c(1:6,8:10)]

# write.csv(egoUp_genesByGroup, file="top10GOtermsUpregulated_geneMembership.csv")
# ionGenes <- unique(unlist(egoUp_genesByGroup_ionOnly))
# 
# ensembl = useEnsembl(biomart="ensembl", dataset="hsapiens_gene_ensembl")
# IDs <- as.character(ionGenes)
# geneUpID <- names(geneList_up)
# geneDownID <- names(geneList_down)
# genedesc_ion <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = IDs, mart =ensembl)
# write.csv(genedesc_ion, file = "ionChannelGenes_description.csv")

# genedesc_Up <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneUpID, mart =ensembl)
# write.csv(genedesc_Up, file = "upregulatedGenes_description.csv")
# genedesc_Down <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneDownID, mart =ensembl)
# write.csv(genedesc_Down, file = "downrgulatedGenes_description.csv")
geneList_all <- as.vector(results_0to8d$log2FoldChange)
names(geneList_all) <- rownames(results_0to8d)
a <- names(geneList_all)
genes_ENTREZID <- bitr(a, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:many mapping between keys and columns
Warning: 10.54% of input gene IDs are fail to map...
names(geneList_all) <- genes_ENTREZID

gene_df <- data.frame(Entrez=names(geneList_all), HGNC=a, FC=geneList_all)
gene_df <- gene_df[abs(gene_df$FC) > 1,]
gene_df$group <- "upregulated"
gene_df$group[gene_df$FC < 0] <- "downregulated"
gene_df$othergroup <- "A"
gene_df$othergroup[abs(gene_df$FC) > 2] <- "B"

formula_res <- compareCluster(Entrez~group+othergroup, data=gene_df, fun="enrichKEGG")
head(as.data.frame(formula_res))
NA

3. Exploring Results

plotMA(res, ylim=c(-2,2))

plotCounts(dds, gene=which.min(res$padj), intgroup="treatment")

Log normalize results


# normalizedCounts <- t( t(counts(dds)) / sizeFactors(dds) )

#log2 normalized counts
rld2 <- rlog(dds, blind = FALSE)
# save(rld2, file = "RLD2_SC1,7,10_Timecourse_hmap.RData")

# load("RLD2_SC1,7,10_Timecourse_hmap.RData")

Clustering

sampleDists <- dist(t(assay(rld2)))
sampleDists
               SC01_day0_rep1 SC01_day0_rep2 SC01_day0_rep3 SC01_day3_rep1 SC01_day3_rep2 SC01_day3_rep3 SC01_day8_rep1
SC01_day0_rep2       20.64176                                                                                          
SC01_day0_rep3       18.19293       20.22538                                                                           
SC01_day3_rep1       49.40193       50.31301       48.91695                                                            
SC01_day3_rep2       49.71034       50.26004       49.81799       18.62486                                             
SC01_day3_rep3       49.76155       51.27548       49.96769       18.78657       18.38885                              
SC01_day8_rep1       62.59359       63.01410       62.85700       32.82065       30.94772       31.44917               
SC01_day8_rep2       61.71897       62.55664       61.74415       31.23053       30.59913       30.62985       18.73472
SC01_day8_rep3       61.80263       62.20672       61.81830       31.97443       30.71802       31.15294       18.44927
SC07_day0_rep1       81.89021       82.20006       81.04377       88.57987       89.20801       89.30811       92.42588
SC07_day0_rep2       81.80699       82.20748       81.28570       88.75296       89.12160       89.21459       92.00426
SC07_day0_rep3       81.87195       81.87247       80.68421       88.82137       89.71360       89.74738       93.26375
SC07_day3_rep1       85.61074       85.98940       84.75355       72.66767       73.41897       73.65191       73.24393
SC07_day3_rep2       86.14496       85.54535       85.04926       73.05334       73.23090       73.98050       73.11989
SC07_day3_rep3       85.64684       86.07900       85.21303       72.75165       72.77774       73.15245       71.75449
SC07_day8_rep1       80.85517       80.83147       79.81064       68.49057       69.16763       69.63491       69.64733
SC07_day8_rep2       81.78387       81.86172       81.53618       68.99246       68.65818       69.49494       67.67811
SC07_day8_rep3       81.96127       82.96786       82.07631       69.32061       69.04146       69.60331       67.94028
SC10_day0_rep1       83.07778       82.99836       82.08754       91.37875       92.05715       92.14807       95.58351
SC10_day0_rep2       82.30597       82.41462       81.52323       90.67633       91.20000       91.27253       94.48343
SC10_day0_rep3       83.38418       83.05678       81.96660       90.82899       91.31496       91.65195       94.96023
SC10_day3_rep1       94.56260       95.04098       94.07558       78.96119       79.09299       79.16448       77.51958
SC10_day3_rep2       94.54421       94.56941       93.82024       78.90942       79.24978       79.39973       77.95598
SC10_day3_rep3       93.64285       93.44536       92.79105       78.81335       79.42332       79.33632       78.07884
SC10_day8_rep1       87.27738       86.98355       86.05409       69.06215       69.88851       69.79785       65.85610
SC10_day8_rep2       87.47739       86.47776       86.25693       69.45772       69.72791       70.04895       65.80600
SC10_day8_rep3       86.88310       86.54205       85.74647       68.89336       69.55039       69.66580       65.73789
               SC01_day8_rep2 SC01_day8_rep3 SC07_day0_rep1 SC07_day0_rep2 SC07_day0_rep3 SC07_day3_rep1 SC07_day3_rep2
SC01_day0_rep2                                                                                                         
SC01_day0_rep3                                                                                                         
SC01_day3_rep1                                                                                                         
SC01_day3_rep2                                                                                                         
SC01_day3_rep3                                                                                                         
SC01_day8_rep1                                                                                                         
SC01_day8_rep2                                                                                                         
SC01_day8_rep3       18.69642                                                                                          
SC07_day0_rep1       91.45517       91.76196                                                                           
SC07_day0_rep2       91.40235       91.45590       18.52372                                                            
SC07_day0_rep3       92.10820       92.37462       19.00827       20.11731                                             
SC07_day3_rep1       72.19122       72.59415       54.91487       55.09966       54.93530                              
SC07_day3_rep2       72.53745       72.64698       55.77395       55.87724       56.04278       20.64662               
SC07_day3_rep3       71.51043       71.58790       55.65100       55.01141       56.47374       20.64008       21.57694
SC07_day8_rep1       68.65155       68.66079       66.69991       67.10675       66.19018       37.27740       37.68364
SC07_day8_rep2       67.75896       67.38623       68.72277       68.18289       69.22524       39.25933       38.47343
SC07_day8_rep3       67.80367       67.67581       68.79488       68.20172       69.41487       38.92660       39.81639
SC10_day0_rep1       94.46929       94.69079       52.01423       52.35396       51.88605       74.67691       75.29208
SC10_day0_rep2       93.50482       93.70891       51.60927       51.84052       51.63913       74.13980       74.80425
SC10_day0_rep3       93.97482       94.02969       53.24466       53.87363       53.18749       74.38570       74.37680
SC10_day3_rep1       76.82909       77.16050       70.15360       69.88040       70.84455       48.48969       48.87908
SC10_day3_rep2       77.23104       77.39437       70.18807       70.00662       70.34552       48.20085       48.52108
SC10_day3_rep3       77.21814       77.37906       70.34590       70.16828       70.11023       49.53767       50.52262
SC10_day8_rep1       64.53354       64.95200       72.74278       72.50885       72.28719       52.32340       53.63493
SC10_day8_rep2       64.74684       64.98592       72.82640       72.54277       72.37472       53.04326       53.43198
SC10_day8_rep3       64.45698       64.88612       72.29703       72.17124       72.00910       52.43546       53.45727
               SC07_day3_rep3 SC07_day8_rep1 SC07_day8_rep2 SC07_day8_rep3 SC10_day0_rep1 SC10_day0_rep2 SC10_day0_rep3
SC01_day0_rep2                                                                                                         
SC01_day0_rep3                                                                                                         
SC01_day3_rep1                                                                                                         
SC01_day3_rep2                                                                                                         
SC01_day3_rep3                                                                                                         
SC01_day8_rep1                                                                                                         
SC01_day8_rep2                                                                                                         
SC01_day8_rep3                                                                                                         
SC07_day0_rep1                                                                                                         
SC07_day0_rep2                                                                                                         
SC07_day0_rep3                                                                                                         
SC07_day3_rep1                                                                                                         
SC07_day3_rep2                                                                                                         
SC07_day3_rep3                                                                                                         
SC07_day8_rep1       38.90605                                                                                          
SC07_day8_rep2       37.56907       23.11405                                                                           
SC07_day8_rep3       37.48277       23.92987       18.61052                                                            
SC10_day0_rep1       75.55110       80.34951       82.90518       83.34079                                             
SC10_day0_rep2       74.64518       79.77306       81.97218       82.21761       17.57404                              
SC10_day0_rep3       75.28027       79.43065       82.34559       82.89798       22.39705       22.72044               
SC10_day3_rep1       47.74997       61.98318       61.89741       61.73654       66.19464       65.54651       66.49570
SC10_day3_rep2       47.94074       61.58165       62.33510       62.54015       65.90692       65.29904       65.89319
SC10_day3_rep3       49.87329       62.11995       63.48771       63.63671       65.62131       65.12390       66.27190
SC10_day8_rep1       52.62745       59.08464       60.79100       60.87934       67.10769       66.51370       67.46528
SC10_day8_rep2       53.02902       59.52211       60.73892       61.37117       67.23442       66.68636       67.59668
SC10_day8_rep3       52.80098       59.01285       60.61734       60.87914       66.48652       65.97014       66.65030
               SC10_day3_rep1 SC10_day3_rep2 SC10_day3_rep3 SC10_day8_rep1 SC10_day8_rep2
SC01_day0_rep2                                                                           
SC01_day0_rep3                                                                           
SC01_day3_rep1                                                                           
SC01_day3_rep2                                                                           
SC01_day3_rep3                                                                           
SC01_day8_rep1                                                                           
SC01_day8_rep2                                                                           
SC01_day8_rep3                                                                           
SC07_day0_rep1                                                                           
SC07_day0_rep2                                                                           
SC07_day0_rep3                                                                           
SC07_day3_rep1                                                                           
SC07_day3_rep2                                                                           
SC07_day3_rep3                                                                           
SC07_day8_rep1                                                                           
SC07_day8_rep2                                                                           
SC07_day8_rep3                                                                           
SC10_day0_rep1                                                                           
SC10_day0_rep2                                                                           
SC10_day0_rep3                                                                           
SC10_day3_rep1                                                                           
SC10_day3_rep2       18.41403                                                            
SC10_day3_rep3       25.81185       23.92363                                             
SC10_day8_rep1       38.30838       36.56170       37.42091                              
SC10_day8_rep2       39.03260       37.15108       37.99369       19.07657               
SC10_day8_rep3       38.30680       36.50232       37.72821       17.65951       18.41347
sampleDistMatrix <- as.matrix( sampleDists )
rownames(sampleDistMatrix) <- paste(rld2$treatment, rld2$cell, sep = " - " )
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
         clustering_distance_rows = sampleDists,
         clustering_distance_cols = sampleDists,
         col = colors)



poisd <- PoiClaClu::PoissonDistance(t(counts(dds)))
samplePoisDistMatrix <- as.matrix( poisd$dd )
rownames(samplePoisDistMatrix) <- paste( dds$dex, dds$cell, sep=" - " )
colnames(samplePoisDistMatrix) <- NULL
pheatmap(samplePoisDistMatrix,
         clustering_distance_rows = poisd$dd,
         clustering_distance_cols = poisd$dd,
         col = colors)


mds <- as.data.frame(colData(rld2))  %>%
         cbind(cmdscale(sampleDistMatrix))
ggplot(mds, aes(x = `1`, y = `2`, color = cell, shape = treatment)) +
  geom_point(size = 3) + coord_fixed() + theme_bw() +
  xlab("PC1") + ylab("PC2") +
  theme(legend.text = element_text(size = 10), 
        plot.title = element_text(size = 14, 
                                  hjust = 0.5, 
                                  face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"),
        # legend.position = "bottom",
        axis.title=element_text(size=12))


# library("genefilter")
topVarGenes <- head(order(rowVars(assay(rld2)), decreasing = TRUE), 5000)
mat  <- assay(rld2)[ topVarGenes, ]
mat  <- mat - rowMeans(mat)
anno <- as.data.frame(colData(rld2)[, c("cell","treatment")])
names(anno) <- c("Cell", "Treatment")
annotation_colors = list(
  Cell = c(SC01="red2", SC07="green2", SC10="blue2"),
  Treatment = c("0"="cyan2", "3"="darkorange", "8"="darkorchid"))
pheatmap(mat, annotation_col = anno, show_rownames = F, show_colnames = F,
         annotation_colors = annotation_colors)

Time series analysis

1 DESeq2 time series analysis

# browseVignettes("rnaseqGene")

ddsTC <- DESeq(dds, test="LRT", reduced = ~ cell + treatment)
using pre-existing size factors
estimating dispersions
found already estimated dispersions, replacing these
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
resTC <- results(ddsTC)
resTC$symbol <- mcols(ddsTC)$symbol
# head(resTC[order(resTC$padj),], 4)

tc <- plotCounts(ddsTC, which.min(resTC$padj), 
                   intgroup = c("treatment","cell"), returnData = TRUE)

ddsTC[which.min(resTC$padj),]
class: DESeqDataSet 
dim: 1 27 
metadata(1): version
assays(4): counts mu H cooks
rownames(1): PDK4
rowData names(35): baseMean baseVar ... deviance maxCooks
colnames(27): SC01_day0_rep1 SC01_day0_rep2 ... SC10_day8_rep2 SC10_day8_rep3
colData names(4): cell treatment group sizeFactor
ggplot(tc,
  aes(x = rep(c(0,3,8), each=9), y = count, color = cell, group = cell)) + 
  geom_point() + geom_smooth(se = FALSE, method = "loess") + scale_y_log10() +
  theme_bw() +
  ggtitle("Time Course Expression of PDK4") +
  labs(x="Time (days)", y="Gene Count") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"), 
        axis.title=element_text(size=12, face="bold"),
        legend.position = "bottom")


resultsNames(ddsTC)
[1] "Intercept"           "cell_SC07_vs_SC01"   "cell_SC10_vs_SC01"   "treatment_3_vs_0"    "treatment_8_vs_0"   
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
betas <- coef(ddsTC)
colnames(betas)
[1] "Intercept"           "cell_SC07_vs_SC01"   "cell_SC10_vs_SC01"   "treatment_3_vs_0"    "treatment_8_vs_0"   
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
topGenes <- head(order(resTC$padj),50)
mat <- betas[topGenes, -c(1,2)]
thr <- 3 
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
pheatmap(mat, breaks=seq(from=-thr, to=thr, length=101),
         cluster_col=FALSE)

NOTE

Original code below produced many messages of No id variables; using all as measure variables; presumably a line for each gene. This is due to the melt function not having any id variables to use.

Rejiggering code not yet finished. Should probably use

# 1.1 ANOVA - compare btwn sublines 
# group <- as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_baseline <- list()
TukeySC07toSC01 <- list()
TukeySC10toSC01 <- list()
TukeySC10toSC07 <- list()
norm_data <- as.data.frame(assay(rld2))[c('SC01_day0_rep1',
                                        'SC01_day0_rep2',
                                        'SC01_day0_rep3',
                                        'SC07_day0_rep1',
                                        'SC07_day0_rep2',
                                        'SC07_day0_rep3',
                                        'SC10_day0_rep1',
                                        'SC10_day0_rep2',
                                        'SC10_day0_rep3')]


######################
### New code by DRT ###
######################
# samp_names <- colnames(norm_data)

# compareSubclones <- function(gene_name, dat=norm_data, samp_names=NULL, group=NULL)
# {
#     if(is.null(group)) group <- as.factor(c(1,1,1,2,2,2,3,3,3))
#     if(is.null(samp_names)) samp_names <- colnames(dat)
#     # dfa = data for analysis
#     dfa <- data.frame(value=as.numeric(t(dat[gene_name,])), group=group)
#     rownames(dfa) <- samp_names
#     fit <- aov(value~group, dfa)
#     anova_baseline <- summary(fit)[[1]][['Pr(>F)']][1]
#     results <- TukeyHSD(fit, conf.level = 0.95)
#     pval <- data.frame(p_adj=results$group[,'p adj'])
#     rownames(pval) <- c("TukeySC07toSC01","TukeySC10toSC01","TukeySC10toSC07")
#     out <- list(anova_baseline = anova_baseline,
#                 pval = pval)
#     # TukeySC07toSC01[gene] <- results$group[,'p adj'][1]
#     # TukeySC10toSC01[gene] <- results$group[,'p adj'][2]
#     # TukeySC10toSC07[gene] <- results$group[,'p adj'][3]    
#     return(out)
# }

# temp <- lapply(rownames(norm_data), compareSubclones)
# anova_pval <- sapply(temp, "[[", 1)
######################
### End new code ###
######################

for (gene in 1:nrow(norm_data)) {
  gene_norm_data <- norm_data[gene,]
  # d3 <- data.frame(y = gene_norm_data, group = group)
  # fit <- lm(y~group, d3)
  gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
                                    value=as.numeric(t(gene_norm_data)))
  gene_norm_data_melt$group <- group
  fit <- aov(value~group, gene_norm_data_melt)
  # anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
  anova_baseline[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
  results <- TukeyHSD(fit, conf.level = 0.95)
  TukeySC07toSC01[gene] <- results$group[,'p adj'][1]
  TukeySC10toSC01[gene] <- results$group[,'p adj'][2]
  TukeySC10toSC07[gene] <- results$group[,'p adj'][3]
}
# print(anova_list)

  
anova_pval <- unlist(anova_baseline) # make array
TukeySC07toSC01_pval <- unlist(TukeySC07toSC01)
TukeySC10toSC01_pval <- unlist(TukeySC10toSC01)
TukeySC10toSC07_pval <- unlist(TukeySC10toSC07)

# Make master dataset with statistics
norm_data_stats <- as.data.frame(norm_data)
norm_data_stats <- cbind(norm_data_stats, anova_pval)
norm_data_stats <- cbind(norm_data_stats, TukeySC07toSC01_pval)
norm_data_stats <- cbind(norm_data_stats, TukeySC10toSC01_pval)
norm_data_stats <- cbind(norm_data_stats, TukeySC10toSC07_pval)

# save(norm_data_stats, file = "subcloneCounts_anova_tukey_DESeq2.RData")

# Identify genes that differ between clones based on 
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_pval < sigThresh)
table(TukeySC07toSC01_pval < sigThresh)
table(TukeySC10toSC01_pval < sigThresh)
table(TukeySC10toSC07_pval < sigThresh)

sigIndecies <- which(norm_data_stats["anova_pval"] < sigThresh)

sigIndeciesAll <- which(norm_data_stats["anova_pval"] < sigThresh & 
                          norm_data_stats["TukeySC07toSC01_pval"] < sigThresh &
                          norm_data_stats["TukeySC10toSC01_pval"] < sigThresh &
                          norm_data_stats["TukeySC10toSC07_pval"] < sigThresh)

sigDiffGenes <- rownames(norm_data_stats[sigIndecies,])
sigDiffGenesAll <- rownames(norm_data_stats[sigIndeciesAll,])

2. ANOVA btwn time points & shared btwn sublines)

group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC01 <- list()
TukeySC01_time0 <- list()
TukeySC01_time3 <- list()
TukeySC01_time8 <- list()
norm_data_SC01time <- as.data.frame(assay(rld2))[c('SC01_day0_rep1',
                                        'SC01_day0_rep2',
                                        'SC01_day0_rep3',
                                        'SC01_day3_rep1',
                                        'SC01_day3_rep2',
                                        'SC01_day3_rep3',
                                        'SC01_day8_rep1',
                                        'SC01_day8_rep2',
                                        'SC01_day8_rep3')]
for (gene in 1:nrow(norm_data_SC01time)) {
  gene_norm_data <- norm_data_SC01time[gene,]
  # d3 <- data.frame(y = gene_norm_data, group = group)
  # fit <- lm(y~group, d3)
  # gene_norm_data_melt <- melt(gene_norm_data)
  gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
                                    value=as.numeric(t(gene_norm_data)))

  gene_norm_data_melt$group <- group
  fit <- aov(value~group, gene_norm_data_melt)
  # anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
  anova_SC01[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
  results <- TukeyHSD(fit, conf.level = 0.95)
  TukeySC01_time0[gene] <- results$group[,'p adj'][1]
  TukeySC01_time3[gene] <- results$group[,'p adj'][2]
  TukeySC01_time8[gene] <- results$group[,'p adj'][3]
  
}
# print(anova_list)
anova_SC01_pval <- unlist(anova_SC01) # make array
TukeySC01_time0_pval <- unlist(TukeySC01_time0)
TukeySC01_time3_pval <- unlist(TukeySC01_time3)
TukeySC01_time8_pval <- unlist(TukeySC01_time8)

# Make master dataset with statistics
norm_data_stats_SC01 <- as.data.frame(norm_data_SC01time)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, anova_SC01_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time0_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time3_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time8_pval)

# save(norm_data_stats_SC01, file = "subcloneCounts_anova_tukey_DESeq2_SC01time.RData")

# Identify genes that differ between clones based on 
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC01_pval < sigThresh)
table(TukeySC01_time0_pval < sigThresh)
table(TukeySC01_time3_pval < sigThresh)
table(TukeySC01_time8_pval < sigThresh)

sigIndecies_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh)

sigIndeciesAll_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh & 
                          norm_data_stats_SC01["TukeySC01_time0_pval"] < sigThresh &
                          norm_data_stats_SC01["TukeySC01_time3_pval"] < sigThresh &
                          norm_data_stats_SC01["TukeySC01_time8_pval"] < sigThresh)

sigDiffGenes_SC01 <- rownames(norm_data_stats_SC01[sigIndecies_SC01,])
sigDiffGenesAll_SC01 <- rownames(norm_data_stats_SC01[sigIndeciesAll_SC01,])
group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC07 <- list()
TukeySC07_time0 <- list()
TukeySC07_time3 <- list()
TukeySC07_time8 <- list()
norm_data_SC07time <- as.data.frame(assay(rld2))[c('SC07_day0_rep1',
                                        'SC07_day0_rep2',
                                        'SC07_day0_rep3',
                                        'SC07_day3_rep1',
                                        'SC07_day3_rep2',
                                        'SC07_day3_rep3',
                                        'SC07_day8_rep1',
                                        'SC07_day8_rep2',
                                        'SC07_day8_rep3')]
for (gene in 1:nrow(norm_data_SC07time)) {
  gene_norm_data <- norm_data_SC07time[gene,]
  # d3 <- data.frame(y = gene_norm_data, group = group)
  # fit <- lm(y~group, d3)
  # gene_norm_data_melt <- melt(gene_norm_data)
  gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
                                    value=as.numeric(t(gene_norm_data)))
  gene_norm_data_melt$group <- group
  fit <- aov(value~group, gene_norm_data_melt)
  # anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
  anova_SC07[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
  results <- TukeyHSD(fit, conf.level = 0.95)
  TukeySC07_time0[gene] <- results$group[,'p adj'][1]
  TukeySC07_time3[gene] <- results$group[,'p adj'][2]
  TukeySC07_time8[gene] <- results$group[,'p adj'][3]
  
}
# print(anova_list)
anova_SC07_pval <- unlist(anova_SC07) # make array
TukeySC07_time0_pval <- unlist(TukeySC07_time0)
TukeySC07_time3_pval <- unlist(TukeySC07_time3)
TukeySC07_time8_pval <- unlist(TukeySC07_time8)

# Make master dataset with statistics
norm_data_stats_SC07 <- as.data.frame(norm_data_SC07time)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, anova_SC07_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time0_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time3_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time8_pval)

# save(norm_data_stats_SC07, file = "subcloneCounts_anova_tukey_DESeq2_SC07time.RData")

# Identify genes that differ between clones based on 
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC07_pval < sigThresh)
table(TukeySC07_time0_pval < sigThresh)
table(TukeySC07_time3_pval < sigThresh)
table(TukeySC07_time8_pval < sigThresh)

sigIndecies_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh)

sigIndeciesAll_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh & 
                          norm_data_stats_SC07["TukeySC07_time0_pval"] < sigThresh &
                          norm_data_stats_SC07["TukeySC07_time3_pval"] < sigThresh &
                          norm_data_stats_SC07["TukeySC07_time8_pval"] < sigThresh)

sigDiffGenes_SC07 <- rownames(norm_data_stats_SC07[sigIndecies_SC07,])
sigDiffGenesAll_SC07 <- rownames(norm_data_stats_SC07[sigIndeciesAll_SC07,])

3 Jack’s method

#Grab all the names from res in the DESeq matrix
topGenes <- which(res$padj <= 0.001)

countMAT <- data.frame(normalizedCounts[topGenes,])

subrl = data.frame(assay(rld2))
rlMAT = data.frame(subrl[topGenes,])

#Labeling rows with ENSG IDs
# countMAT$ensembl_gene_id = row.names(countMAT)
# countMAT$padj = res[topGenes,"padj"]

rlMAT$ensembl_gene_id = row.names(rlMAT)
rlMAT$padj = res[topGenes,"padj"]

# library(biomaRt)
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(rlMAT)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
#                 filters= "ensembl_gene_id",
#                 values=genes,
#                 mart=mart)

#Check if data fits a normal distribution
# plot(density(c(as.matrix(countMAT[,1:27]))))
plot(density(c(as.matrix(rlMAT[,1:27]))))


#rlMAT follows a normal distribution, therefore we will use this in the heatmap construction
#Labeling df with hgnc symbols
GE_data <- merge(G_list, rlMAT, by = "ensembl_gene_id")

#Making rownames unique hgnc symbols
rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
GE_data = GE_data[order(GE_data$padj),]


#Averaging rld between trials
Acol <- c("SC01_day0",
          "SC01_day3",
          "SC01_day8",
          "SC07_day0",
          "SC07_day3",
          "SC07_day8",
          "SC10_day0",
          "SC10_day3",
          "SC10_day8")
for(i in 1:length(Acol)){
  j = 2+i
  k = 2+3*i
  GE_data[,Acol[i]] = rowMeans(GE_data[,c(j:k)])
}


#Calculating fold changes across conditions in a triangular matrix form
GE_mean = GE_data[,c(1,2,30:39)]
DEProc = GE_mean
startcol = 4
endcol = 12

allFC <- function(DEProc,startcol,endcol){ 
  GE_fold = DEProc[,-c(startcol:endcol)]
  colvec = colnames(DEProc)[startcol:endcol]
  
  #Last index is a self comparison and is removed
  for(k in 1:(length(colvec)-1)){
    #Start with column that is 1 away from index 
    for(j in (k+1):length(colvec)){
      compnam = paste0(colvec[j],"/",colvec[k])
      #Loop through each gene/row  
      for(i in 1:nrow(DEProc)){
        f = DEProc[i,colvec[j]]
        h = DEProc[i,colvec[k]]
        
        #Capture upregulation and down regulation
        if(f>h){
          GE_fold[i,compnam] = 2^(f-h)
        }else{
          GE_fold[i,compnam] = -2^(h-f)
        }
        
      }
    }
  }
  
  return(GE_fold)
  
}

#Subset gene, then plot, then save plot
#Perhaps make heatmaps with scaled z scores
#Is there a way to consolidate replicate z scores? Geometric mean? 
#Regular mean, then scale.

# ImpRat = colnames(GE_fold)[c(4,5,6,9,12,14,17,21,24,25,26,27,30,32,36,37,38,39)]

#Listing of all important comparisons?
ImpRat = c("SC01_day3/SC01_day0", "SC01_day8/SC01_day3", "SC01_day8/SC01_day0", 
           "SC07_day3/SC07_day0", "SC07_day8/SC07_day3", "SC07_day8/SC07_day0", 
           "SC10_day3/SC10_day0", "SC10_day8/SC10_day3", "SC10_day8/SC10_day0", 
           "SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0",
           "SC07_day3/SC01_day3", "SC10_day3/SC01_day3", "SC10_day3/SC07_day3",
           "SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8" )
Imp_fold = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", ImpRat)]
Imp_fold2 = Imp_fold[rowSums(abs(Imp_fold[,4:21])>=1.5)>=1,]

# write.table(Imp_fold,"SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t", row.names=F)

Imp_fold = read.delim("SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t")

#Subset the LF mean of important genes from Log2 Fold Change (LFC) comparison data frame.
GE_Imp = subset(GE_mean,GE_mean$ensembl_gene_id%in%Imp_fold2$ensembl_gene_id)

Necro = read.delim("KEGGNecroptosis_hsa04217_06-25-18.txt", header=T, stringsAsFactors = F)
Necro = Necro[rowSums(is.na(Necro)) == 0, ]
DE_Necro = merge(GE_Imp, Necro, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Necro) = make.names(DE_Necro[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Necro[3:29],cluster_cols = TRUE)
# write.table(DE_Necro, "KEGGNecroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)


Apop = read.delim("KEGGApoptosis_hsa04210_06-25-18.txt", header=T, stringsAsFactors = F)
Apop = Apop[rowSums(is.na(Apop)) == 0, ]
DE_Apop = merge(GE_Imp), Apop, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Apop) = make.names(DE_Apop[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Apop[3:29],cluster_cols = TRUE, scale = "row")
# write.table(DE_Apop, "KEGGApoptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)

Ferr = read.delim("KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
Ferr = Ferr[rowSums(is.na(Ferr)) == 0, ]
DE_Ferr = merge(GE_Imp, Ferr, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Ferr) = make.names(DE_Ferr[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Ferr[4:12],cluster_cols=FALSE, scale = "row")
# write.table(DE_Ferr, "KEGGFerroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)

4. Different LC comparisons. Between subclones and at baseline vs idling.

Zscore heatmaps of relevant comparisons can be made as in above to visualize.

#USES ABOVE CODE TO LINE 280. Run that pseudo-function.

# library(pheatmap)
#Comparisons of difEx between subclones at baseline and idling
BetweenBase = c("SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0")
BetweenIdle = c("SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8")
 

#Unsure of how strict to make the cutoff. Should all the genes between clones be differentially expressed (3) or is a single difference sufficient?
Btw_b = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenBase)]
Btw_b1 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=1,]
Btw_b2 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=2,]
Btw_b3 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=3,]

Btw_i = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenIdle)]
Btw_i1 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=1,]
Btw_i2 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=2,]
Btw_i3 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=3,]

#This does not account for same direction of change. This can be plotted in a heatmap to view.
#Members that were "lost" by the baseline condition at being different. Were no longer found diffEx between the subclones when comparing baseline to idling DEGs.
LostDEG_b_1 = subset(Btw_b1,!Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
LostDEG_b_2 = subset(Btw_b2,!Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
LostDEG_b_3 = subset(Btw_b3, !Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)

##Make heatmap
LostDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_3$ensembl_gene_id)
row.names(LostDEG_b_3_mean) = make.names(LostDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")

#Members that remained different. 
StaticDEG_b_1 = subset(Btw_b1,Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
StaticDEG_b_2 = subset(Btw_b2,Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
StaticDEG_b_3 = subset(Btw_b3, Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)

##Some HGNC_symbols are duplicates! I switched to ensembl_gene_id to fix.
StaticDEG_i_3 = subset(Btw_i3, Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)


##Make heatmap
StaticDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%StaticDEG_b_3$ensembl_gene_id)
row.names(StaticDEG_b_3_mean) = make.names(StaticDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(StaticDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")


#Members that "gained" differences between the subclones in idling.  
GainDEG_i_1 = subset(Btw_i1, !Btw_i1$ensembl_gene_id%in%Btw_b1$ensembl_gene_id)
GainDEG_i_2 = subset(Btw_i2, !Btw_i2$ensembl_gene_id%in%Btw_b2$ensembl_gene_id)
GainDEG_i_3 = subset(Btw_i3, !Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)

##Make heatmap
GainDEG_i_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%GainDEG_i_3$ensembl_gene_id)
row.names(GainDEG_i_3_mean) = make.names(GainDEG_i_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(GainDEG_i_3_mean[4:12],cluster_cols=FALSE, scale = "row")


#Members that were differentially expressed between idling (8day) and baseline within subclones. Those with shared diffEx may be convergent across multiple subclones depending on direction of expresison change.
Endpoint = c("SC01_day8/SC01_day0", "SC07_day8/SC07_day0", "SC10_day8/SC10_day0")
BtoIdle = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", Endpoint)]
BtoIdle_1 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=1,]
BtoIdle_2 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=2,]
BtoIdle_3 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=3,]

##Make heatmap
BtoIdle_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%BtoIdle_2$ensembl_gene_id)
row.names(BtoIdle_2_mean) = make.names(BtoIdle_2_mean[,"hgnc_symbol"], unique = TRUE)

BtoIdle_2_mean_incExp = BtoIdle_2_mean[which(BtoIdle_2_mean$SC01_day0 < BtoIdle_2_mean$SC01_day8),]
BtoIdle_2_mean_incExp = BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC07_day0 < BtoIdle_2_mean_incExp$SC07_day8),]
BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC10_day0 < BtoIdle_2_mean_incExp$SC10_day8),]

LostDEG_b_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_2$ensembl_gene_id)
row.names(LostDEG_b_2_mean) = make.names(LostDEG_b_2_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_2_mean[4:12],cluster_cols=FALSE, scale = "row")

BtoIdleIncExp_DEbetweenSCs = BtoIdle_2_mean_incExp[which(row.names(BtoIdle_2_mean_incExp) %in% row.names(LostDEG_b_2_mean)),]

pheatmap(BtoIdle_2_mean_incExp[4:12],cluster_cols=FALSE, scale = "row")

# library(devtools)
# # install_github("wjawaid/enrichR")
# library(enrichR)
dbs <- listEnrichrDbs()
head(dbs)
dbs <- c("GO_Molecular_Function_2015", "GO_Cellular_Component_2015", "GO_Biological_Process_2015")

enriched <- enrichr(row.names(BtoIdle_2_mean_incExp), dbs)

View(enriched[["GO_Molecular_Function_2015"]])
View(enriched[["GO_Cellular_Component_2015"]])
View(enriched[["GO_Biological_Process_2015"]])

enriched_MF_sig <- enriched[["GO_Molecular_Function_2015"]][enriched[["GO_Molecular_Function_2015"]]$Adjusted.P.value<0.05,]
enriched_MF_sig_df <- data.frame(enriched_MF_sig[,c(1,4,9)])
write.csv(enriched_MF_sig_df, "enriched_MF_significant.csv")

enriched_BP_sig <- enriched[["GO_Biological_Process_2015"]][enriched[["GO_Biological_Process_2015"]]$Adjusted.P.value<0.05,]
enriched_BP_sig_df <- data.frame(enriched_BP_sig[,c(1,4,9)])
# write.csv(enriched_BP_sig_df, "enriched_BP_significant.csv")

Gini_scGenes <- c("APOE", "BCAN", "CES1", "CITED1",
                  "CPM", "CTSF", "DCT", "EDNRB", 
                  "EGR1", "ESRP1", "FSTL1", "MALAT1",
                  "MAP2K6", "MCF2L", "MLANA", "MXD4",
                  "OCA2", "PMEL", "SEMA6A", "SNAI2",
                  "SOX4", "TSPAN10")
enriched_sc <- enrichr(Gini_scGenes, dbs)

row.names(BtoIdle_2_mean_incExp) %in% Gini_scGenes

Rest of Jack’s Analysis

#Visually inspect trending members from heatmaps.
#Plots of specific trending members?
p <- ggplot(data=df2, aes(x=dose, y=len, fill=supp)) +
  geom_bar(stat="identity", color="black", position=position_dodge())+
  theme_minimal()

NOTE: code below reuses object names… WILL OVERWRITE!

#GLM Coef Heatmap.
betas <- coef(dds)
topGenes <- which(res$padj <= 0.001)
mat <- data.frame(betas[topGenes,])
mat$ensembl_gene_id = row.names(mat)
mat$padj = res[topGenes,"padj"]
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(mat)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
#                 filters= "ensembl_gene_id",
#                 values=genes,
#                 mart=mart)

# GE_data <- merge(mat, G_list, by = "ensembl_gene_id")
# rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
# GE_data = GE_data[order(GE_data$padj),]


#Sorting script to pick out entries greater than or less than +-1
eg = c()
for(i in 3:10){
  g = which(GE_data[,i] > 3 | GE_data[,i] < -3)
  eg = c(eg,g)
}
eg = unique(eg)

mat = GE_data[eg,-c(1:2,11,12)]
thr <- 3 
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
# library(pheatmap)
pheatmap(mat, cluster_cols = FALSE)

# ssdg = sdg[1:1000, ]
dim(sdg)
head(sdg)
LS0tCnRpdGxlOiAiUk5Bc2VxIERFU2VxMiBUaW1lIENvdXJzZSIKYXV0aG9yOiAiQ29yZXkgSGF5Zm9yZCAobW9kaWZlZCBmcm9tIEphY2spIgpkYXRlOiAiTm92ZW1iZXIgMjAxOC1KYW51YXJ5IDIwMTkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBEUlQgbW9kaWZpZWQgZmlsZSB0byB3b3JrIG9uIGFueSBjb21wdXRlcgoyMDIyLTAxLTE3ClNvbWUgZmlsZXMgYXBwZWFyZWQgdG8gYmUgbWlzc2luZyB0byBydW4gdGhpcyBub3RlYm9vay4gVHJ5aW5nIHRvIGdldCBhbGwgdG8gcnVuCgojIyBPdmVydmlldwpUaGlzIGlzIGEgcGlwZWxpbmUgZm9yIGRpZmZlcmVudGlhbCBhbmFseXNpcyBvZiBSTkFTZXEgZGF0YSBmcm9tIFNLTUVMNSBzdWJsaW5lcyB1c2luZyBERVNlcTIgc3RhdGlzdGljYWwgcGFja2FnZS4gVGhyZWUgc3VibGluZXM6IFNDMDEgKCpyZWdyZXNzaW5nKiksIFNDMDcgKCpzdGF0aW9uYXJ5KikgYW5kIFNDMTAgKCpleHBhbmRpbmcqKSB3ZXJlIGFuYWx5emVkIGZvciBnZW5lIGV4cHJlc3Npb24gZGlmZmVyZW5jZXMuIEluIGFkZGl0aW9uLCB0aW1lIGNvdXJzZSBjaGFuZ2VzIGluIDh1TSBQTFg0NzIwIHdlcmUgYWxzbyBwZXJmb3JtZWQgZm9yIGVhY2ggc3VibGluZS4gVGltZSBwb2ludHMgYXJlOiAwLCAzZCwgOGQuIFRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgd2lsbCBiZSBwZXJmb3JtZWQgYmFzZWQgb24gdGhlIGNvbnRyYXN0cyBkZWZpbmVkIGJlbG93LiAKR2VuZXJhbCBzdGVwcyBmb3IgdGhlIGFuYWx5c2lzIGFyZToKICAKIyMjMS4gUmVhZCBjb3VudHMgdGFibGU6IAorIENvdWxkIGJlIHJlYWQgZGlyZWN0bHkgYXMgYSBjc3YvdHh0IGZpbGUuIAorIEFsaWdubWVudCBhbmQgcmVhZCBjb3VudHMgY291bGQgYmUgZG9uZSB3aXRoaW4gUiBlbnZpcm9ubWVudCB0byBjcmVhdGUgcmVhZCBjb3VudHMgdGFibGUuIAoxLiBEZWZpbmUgd29ya2luZyBkaXJlY3RvcnksIGxvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcy4gCjIuIEdldCByZWFkIGNvdW50cyB0YWJsZS4gClJlYWQgdGhlIHJhdyBjb3VudHMgZmlsZSBwcm9jZXNzZWQgYnkgZmVhdHVyZUNvdW50cy4gVGhlIGZhc3RxIGZpbGVzIHdlcmUgYWxpZ25lZCB3aXRoIEhpU2F0MiwgYW5kIHRoZSByZWFkIGNvdW50cyB3ZXJlIG9idGFpbmVkIHVzaW5nIGZlYXR1cmVDb3VudHMgb2YgUnN1YnJlYWQgcGFja2FnZXMuCgpgYGB7ciBJbnN0YWxsYXRpb24sIGV2YWw9RkFMU0V9CnBrZ3MgPC0gYygiQmlvY01hbmFnZXIiLCJERVNlcTIiLCJvcmcuSHMuZWcuZGIiLCJjbHVzdGVyUHJvZmlsZXIiLCJIRE8uZGIiLAogICAgICAgICAgInBoZWF0bWFwIiwiZ2duZXdzY2FsZSIsIlBvaUNsYUNsdSIsImVucmljaFIiLCJndGFibGUiLCJSbWlzYyIpCnNvdXJjZSgiZ2V0UmVxZFBrZ3MuciIpCmdldFJlcWRQa2dzKHBrZ3MpCmBgYAoKCmBgYHtyIFNldHVwfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoZXhwcj17CiAgICBsaWJyYXJ5KHBseXIpCiAgICBsaWJyYXJ5KGRwbHlyKQogICAgbGlicmFyeShnZ3Bsb3QyKQogICAgbGlicmFyeShnZ25ld3NjYWxlKQogICAgbGlicmFyeShyZXNoYXBlMikKICAgIGxpYnJhcnkoREVTZXEyKQogICAgbGlicmFyeShnZ3JlcGVsKQogICAgbGlicmFyeShwaGVhdG1hcCkKICAgIGxpYnJhcnkob3JnLkhzLmVnLmRiKQogICAgbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCiAgICBsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKQogICAgbGlicmFyeShlbnJpY2hSKQogICAgbGlicmFyeShiaW9tYVJ0KQogICAgbGlicmFyeShSbWlzYykKfSkKClNBVkVGSUxFUyA8LSBGQUxTRQpgYGAKCmBgYHtyLCBlY2hvPVRSVUV9CmQgPC0gcmVhZC5jc3YoIi4uL2RhdGEvZmVhdHVyZUNvdW50c19tYXRyaXhfYWxsLmNzdiIsIGhlYWRlcj1ULCBzZXA9IiwiKQoKI1JlbmFtZSBjb2x1bW5zCmNvbHMgPC0gYygiZW5zZW1ibF9nZW5lX2lkIiwgIlNDMDFfZGF5MF9yZXAxIiwgIlNDMDFfZGF5MF9yZXAyIiwgIlNDMDFfZGF5MF9yZXAzIiwKICAgICAgICAgICJTQzAxX2RheTNfcmVwMSIsICJTQzAxX2RheTNfcmVwMiIsICJTQzAxX2RheTNfcmVwMyIsCiAgICAgICAgICAiU0MwMV9kYXk4X3JlcDEiLCAiU0MwMV9kYXk4X3JlcDIiLCAiU0MwMV9kYXk4X3JlcDMiLAogICAgICAgICAgIlNDMDdfZGF5MF9yZXAxIiwgIlNDMDdfZGF5MF9yZXAyIiwgIlNDMDdfZGF5MF9yZXAzIiwKICAgICAgICAgICJTQzA3X2RheTNfcmVwMSIsICJTQzA3X2RheTNfcmVwMiIsICJTQzA3X2RheTNfcmVwMyIsCiAgICAgICAgICAiU0MwN19kYXk4X3JlcDEiLCAiU0MwN19kYXk4X3JlcDIiLCAiU0MwN19kYXk4X3JlcDMiLAogICAgICAgICAgIlNDMTBfZGF5MF9yZXAxIiwgIlNDMTBfZGF5MF9yZXAyIiwgIlNDMTBfZGF5MF9yZXAzIiwKICAgICAgICAgICJTQzEwX2RheTNfcmVwMSIsICJTQzEwX2RheTNfcmVwMiIsICJTQzEwX2RheTNfcmVwMyIsCiAgICAgICAgICAiU0MxMF9kYXk4X3JlcDEiLCAiU0MxMF9kYXk4X3JlcDIiLCAiU0MxMF9kYXk4X3JlcDMiKQpuYW1lcyhkKSA8LSBjb2xzCmVuc2VtYmwgPC0gdXNlRW5zZW1ibCgiZW5zZW1ibCIsIG1pcnJvcj0idXNlYXN0IikKbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKZ2VuZXMgPC0gZCRlbnNlbWJsX2dlbmVfaWQKR19saXN0IDwtIGdldEJNKGF0dHJpYnV0ZXM9IGMoImVuc2VtYmxfZ2VuZV9pZCIsImhnbmNfc3ltYm9sIiksCiAgICAgICAgICAgICAgICBmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwKICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKICAgICAgICAgICAgICAgIG1hcnQ9bWFydCkKCkdFX2RhdGEgPC0gbWVyZ2UoZCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQpkIDwtIEdFX2RhdGFbLCAtMV0KZCA8LSBkW2MoMjgsIHNlcSgxOjI3KSldCnJvd25hbWVzKGQpIDwtIG1ha2UubmFtZXMoZCRoZ25jX3N5bWJvbCwgdW5pcXVlID0gVCkKZCA8LSBkWywgMjoyOF0KCiMgcmVtb3ZlIGdlbmVzIHdpdGggPDUgY291bnRzIGluIGFsbCBzYW1wbGVzCmQgPC0gZFthcHBseShkLCAxLCBmdW5jdGlvbih4KSBhbGwoeCA+IDUpKSxdCgoKY291bnRkYXRhIDwtIGQKIyBiYXNlbGluZSA8LSBjKDEsMiwzLDEwLDExLDEyLDE5LDIwLDIxKQojIHRyZWF0M2QgIDwtIGMoNCw1LDYsMTMsMTQsMTUsMjIsMjMsMjQpCiMgdHJlYXQ4ZCAgPC0gYyg3LDgsOSwxNiwxNywxOCwyNSwyNiwyNykKIyAjIGRlZmluZSB0aGUgZ3JvdXBzIGJ5IHN1YmNsb25lcwojIHNjMDEgPC0gYyhiYXNlbGluZVsxOjNdLCB0cmVhdDNkWzE6M10sIHRyZWF0OGRbMTozXSkKIyBzYzA3IDwtIGMoYmFzZWxpbmVbNDo2XSwgdHJlYXQzZFs0OjZdLCB0cmVhdDhkWzQ6Nl0pCiMgc2MxMCA8LSBjKGJhc2VsaW5lWzc6OV0sIHRyZWF0M2RbNzo5XSwgdHJlYXQ4ZFs3OjldKQojICMgR2V0IHRoZSBjb3VudGRhdGEgc3BlY2lmaWMgdG8gY29uZGl0aW9uczogCiMgIyBjb3VudGRhdGEgPC0gY291bnRkYXRhWyxjKGJhc2VsaW5lKV0gCiMgcm93bmFtZXMoY291bnRkYXRhKSA8LSBkWywiZW5zZW1ibF9nZW5lX2lkIl0KaGVhZChjb3VudGRhdGEpCmBgYAoKIyMjIElkZW50aWZ5aW5nIGRpZmZlcmVudCBpb24gY2hhbm5lbCBnZW5lIGxpc3RzCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmNvdW50ZGF0YV9oaXN0YW1pbmUgPSBjb3VudGRhdGFbYygiSFJIMSIsIkhSSDIiLCJIUkgzIiwiSFJINCIpLF0KY291bnRkYXRhX0lQcyA9IGNvdW50ZGF0YVtjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpLF0KY291bnRkYXRhX0lQcmVsID0gY291bnRkYXRhW2MoIklUUFIxIiwgIklUUFIyIiwgIklUUFIzIiwgIlBMQ0cxIiwiREdLQSIsICJER0tCIiwgIkRHS0QiLCAiREdLRSIsICJER0tHIiwgIkRHS0giLCAiREdLSSIsICJER0tLIiwgIkRHS1EiLCAiREdLWiIsICJQSUszQ0EiLCAiUElLM0NCIiwgIlBJSzNDRCIsICJQSUszQ0ciLCAiUElLM0MyQSIsICJQSUszQzJCIiwgIlBJSzNDMkciLCAiUElLM0MzIiksXQpjb3VudGRhdGFfbXVzYyA9IGNvdW50ZGF0YVtjKCJDSFJNMSIsICJDSFJNMiIsICJDSFJNMyIsICJDSFJNNCIsICJDSFJNNSIpLF0KY291bnRkYXRhX2dsdXQgPSBjb3VudGRhdGFbYygiR1JNMSIsICJHUk0yIiwgIkdSTTMiLCAiR1JNNCIsICJHUk01IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JNNiIsICJHUk03IiwgIkdSTTgiLCAiR1JJQTEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JJQTIiLCAiR1JJQTMiLCAiR1JJQTQiLCAiR1JJRDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklEMiIsICJHUklLMSIsICJHUklLMiIsICJHUklLMyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklLNCIsICJHUklLNSIsICJHUklOMSIsICJHUklOMkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOMkIiLCAiR1JJTjJDIiwgIkdSSU4yRCIsICJHUklOM0EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOM0IiKSxdCgojIyMgUGxvdCBJUCMgZGF0YQojIHNvdXJjZSgic3VtbWFyeVNFLlIiKQpJUHNfbWF0Y2ggPSBjb2xzcGxpdChjb2xuYW1lcyhjb3VudGRhdGFfSVBzKSwgcGF0dGVybiA9ICJfIiwgbmFtZXMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIikpCklQc19wbG90ID0gY2JpbmQoSVBzX21hdGNoLCB0KGNvdW50ZGF0YV9JUHMpKQpJUHNfbWVsdCA9IG1lbHQoZGF0YSA9IElQc19wbG90LCBpZC52YXJzID0gYygiUG9wdWxhdGlvbiIsICJUaW1lIiwgIlJlcGxpY2F0ZSIpLCBtZWFzdXJlLnZhcnMgPSBjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpKQpJUHNfZGF0ID0gUm1pc2M6OnN1bW1hcnlTRShJUHNfbWVsdCwgbWVhc3VyZXZhciA9ICJ2YWx1ZSIsIGdyb3VwdmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJ2YXJpYWJsZSIpKQpJUHNfZGF0JFRpbWUgPSBhcy5udW1lcmljKGdzdWIoIlteWzpkaWdpdDpdXSIsIiIsSVBzX2RhdCRUaW1lKSkKSVBzX2RhdF9zdWIgPSBzdWJzZXQoSVBzX2RhdCwgdmFyaWFibGUgJWluJSBjKCJJVFBSMiIsIklUUFIzIikpCklQc19nZ3Bsb3RlZCA8LSBnZ3Bsb3QoSVBzX2RhdF9zdWIsIGFlcyh4PVRpbWUsIHk9dmFsdWUsIGdyb3VwID0gaW50ZXJhY3Rpb24odmFyaWFibGUsIFBvcHVsYXRpb24pKSkgKyBnZW9tX2xpbmUoc2l6ZT0xLjUsIGFlcyhjb2xvciA9IFBvcHVsYXRpb24sIGxpbmV0eXBlID0gdmFyaWFibGUpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgYWVzKGNvbG9yID0gUG9wdWxhdGlvbikpICsKZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj12YWx1ZS1zZCwgeW1heD12YWx1ZStzZCwgY29sb3IgPSBQb3B1bGF0aW9uKSwgd2lkdGg9LjIsIHNpemU9MS41KSArCnRoZW1lX2J3KCkgKyB4bGFiKCJUaW1lIChkYXlzKSIpICsgeWxhYigiR2VuZSBDb3VudHMiKSArCmdndGl0bGUoIklQMyBnZW5lIHJlY2VwdG9ycyIpICsKdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0iYm9sZCIpLAogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSkKCklQc19nZ3Bsb3RlZAojIGdnc2F2ZSgiSVAzUjJfM19UaW1lU2VyaWVzUk5Bc2VxX2Nsb25lcy5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCmBgYAoKIyMjMi4gQ29udmVydCBjb3VudHMgdGFibGUgdG8gREVTZXEyIG9iamVjdC4gCkNvbnZlcnQgY291bnRzIHRhYmxlIHRvIG9iamVjdCBmb3IgREVTZXEyIG9yIGFueSBvdGhlciBhbmFseXNpcyBwaXBlbGluZS4gVGhpcyBzdGVwIHdpbGwgcmVxdWlyZSB0byBwcmVwYXJlIGRhdGEgb2JqZWN0IGluIGEgZm9ybSB0aGF0IGlzIHN1aXRhYmxlIGZvciBhbmFseXNpcyBpbiBERVNlcTIgcGlwZWxpbmU6IHdlIHdpbGwgbmVlZCB0aGUgZm9sbG93aW5nIHRvIHByb2NlZWQ6CiAgCiAgKyBjb3VudGRhdGE6IGEgdGFibGUgd2l0aCB0aGUgcmVhZC9mcmFnbWVudCBjb3VudHMuIAorIGNvbGRhdGE6IGEgdGFibGUgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlcy4gCgpVc2luZyB0aGUgbWF0cml4IG9mIGNvdW50cyBhbmQgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZSwgd2UgbmVlZCB0byBjb25zdHJ1Y3QgdGhlIERFU2VxRGF0YVNldCBvYmplY3QsIGZvciB3aGljaCB3ZSB3aWxsIHVzZSBERVNlcURhdGFTZXRGcm9tTWF0cml4Li4uLi4KCiMjIyMgMS4gRGVmaW5lIHRoZSBzYW1wbGVzIGFuZCB0cmVhdG1lbnQgY29uZGl0aW9ucy4gCmBgYHtyfQpjb25kaXRpb24gPC0gYygiMCIsICIzIiwgIjgiKQp0cmVhdG1lbnQgPC0gcmVwKGNvbmRpdGlvbiwgZWFjaD0zKSAjIFRocmVlIGJpb2xvZ2ljYWwgcmVwbGljYXRlcwp1bmlxdWUodHJlYXRtZW50KQpjZWxsIDwtIGMoIlNDMDEiLCAiU0MwNyIsIlNDMTAiKSAjc3VibGluZXMgdXNlZCBmb3IgdGhlIGFuYWx5c2lzCmNlbGxOYW1lIDwtIHJlcChjZWxsLCBlYWNoPTMpCgpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoY2VsbD1yZXAoY2VsbE5hbWUpLCB0cmVhdG1lbnQ9cmVwKHRyZWF0bWVudCwgZWFjaD0zKSkKZ3JvdXAgPSBmYWN0b3IocGFzdGUoY29sZGF0YSRjZWxsLCBjb2xkYXRhJHRyZWF0bWVudCwgc2VwPSIuIikpCmNvbGRhdGEkZ3JvdXAgPSBncm91cApgYGAKCiMjIyMgMi4gY29uc3RydWN0IHRoZSBERVNlcURhdGFTZXQgb2JqZWN0IGZyb20gdGhlIG1hdHJpeCBvZiBjb3VudHMgYW5kIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gdGFibGUuIApEZXNjcmliZWQgYWJvdmUgYXJlOiBjb3VudGRhdGEtIHJhdyBjb3VudHMsIGNvbGRhdGE6IHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZS4gCmBgYHtyfQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBjb2xkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGNlbGwgKyB0cmVhdG1lbnQgKyBjZWxsOnRyZWF0bWVudCkKZGRzCmBgYAoKIyMjMy4gRXhwbG9yYXRvcnkgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24uClRoZXJlIGFyZSB0d28gc2VwYXJhdGUgc3RlcHMgaW4gdGhlIHdvcmtmbG93OyB0aGUgb25lIHdoaWNoIGludm9sdmVzIGRhdGEgdHJhbnNmb3JtYXRpb25zIGluIG9yZGVyIHRvIHZpc3VhbGl6ZSBzYW1wbGUgcmVsYXRpb25zaGlwcyBhbmQgdGhlIHNlY29uZCBzdGVwIGludm9sdmVzIHN0YXRpc3RpY2FsIHRlc3RpbmcgbWV0aG9kcyB3aGljaCByZXF1aXJlcyB0aGUgb3JpZ2luYWwgcmF3IGNvdW50cy4gCgojIyMjIDEuIFByZS1maWx0ZXJpbmcgYW5kIG5vcm1hbGl6YXRpb24uIApQcmUtZmlsdGVyaW5nIGFuZCBub3JtYWxpemF0aW9uIGlzIHJlcXVpcmVkIHRvIHJlbW92ZSBsb3dseSBleHByZXNzZWQgZ2VuZXMuIAoKYGBge3J9CmRkczIgPC0gZGRzW3Jvd1N1bXMoY291bnRzKGRkcykpID4gMTgsIF0gIyByZW1vdmUgcm93cyB3aXRoIG1pbmltdW0gb2YgMiByZWFkIHBlciBjb25kaXRpb24KCm5yb3coZGRzMikKIyBzYXZlKGRkczIsIGZpbGUgPSAiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCiMgbG9hZCgiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCgpgYGAKCiMjIyMgMi4gVmlzdWFsaXplIHNhbXBsZS10by1zYW1wbGUgZGlzdGFuY2VzLiAKV2UgY291bGQgdXNlIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgdG8gdmlzdWFsaXplIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzYW1wbGVzLiAKYGBge3J9CnJsZCA8LSBybG9nKGRkczIsIGJsaW5kID0gRkFMU0UpCiMgc2F2ZShybGQsIGZpbGUgPSAiUkxEX1NDLTEsNywxMF8wLDMsOGRfMjAxODA3MDEuUkRhdGEiKQojIGxvYWQoIlJMRF9TQy0xLDcsMTBfMCwzLDhkXzIwMTgwNzAxLlJEYXRhIikKcGxvdFBDQShybGQsIGludGdyb3VwID0gYygiY2VsbCIsICJ0cmVhdG1lbnQiKSwgbnRvcD01MDAwKQoKIyMgVXNlIHByY29tcCBmdW5jdGlvbgojIENvbG9yZWQgYnkgY2VsbCBsaW5lLCBzaGFwZSBieSB0aW1lIHBvaW50LCBsaW5lcyBjb25uZWN0aW5nIHRpbWUKcGNhX0RFc2VxIDwtIHByY29tcCh0KGFzc2F5KHJsZCkpKQpwY2FfREVzZXFfcGVyYyA8LSByb3VuZCgxMDAqcGNhX0RFc2VxJHNkZXZeMi9zdW0ocGNhX0RFc2VxJHNkZXZeMiksMSkKcGNhX0RFc2VxX2RmIDwtIGRhdGEuZnJhbWUoUEMxID0gcGNhX0RFc2VxJHhbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgUEMyID0gcGNhX0RFc2VxJHhbLDJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlID0gY29sbmFtZXMoYXNzYXkocmxkKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGwubGluZSA9IHJlcChjKCJTQzAxIiwgIlNDMDciLCAiU0MxMCIpLCBlYWNoID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRheSA9IHJlcChjKCJEYXkwIiwgIkRheTMiLCAiRGF5OCIpLCBlYWNoID0gMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxpY2F0ZSA9IHJlcChjKCJSZXAxIiwgIlJlcDIiLCAiUmVwMyIpLCB0aW1lcz05KSkKCnBjYV9ERXNlcV9tZWFucyA8LSBkZHBseShwY2FfREVzZXFfZGYsIC4oY2VsbC5saW5lLCBkYXkpLCBzdW1tYXJpc2UsIG1lYW5QQzEgPSBtZWFuKFBDMSksIG1lYW5QQzIgPSBtZWFuKFBDMikpCgpnZ3Bsb3QocGNhX0RFc2VxX2RmLCBhZXMoUEMxLFBDMiwgY29sb3IgPSBjZWxsLmxpbmUpKSsKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGRheSksIHNpemU9NSkgKwogIGdlb21fcGF0aChkYXRhID0gcGNhX0RFc2VxX21lYW5zLCAKICAgICAgICAgICAgYWVzKHg9bWVhblBDMSwgeT1tZWFuUEMyLAogICAgICAgICAgICAgICAgY29sb3I9Y2VsbC5saW5lKSwgYXJyb3cgPSBhcnJvdygpLAogICAgICAgICAgICBzaXplID0gMikgKwogIGxhYnMoeD1wYXN0ZTAoIlBDMSAoIixwY2FfREVzZXFfcGVyY1sxXSwiJSB2YXJpYW5jZSkiKSwgeT1wYXN0ZTAoIlBDMiAoIixwY2FfREVzZXFfcGVyY1syXSwiJSB2YXJpYW5jZSkiKSkgKwogIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQQ0EgLSBTdWJjbG9uZXMgaW4gVGltZSIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQogIAoKCmBgYAoKIyMjIDQuIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzLiAKQWx3YXlzIG1ha2Ugc3VyZSB0byB1c2UgdGhlIHVubm9ybWFsaXplZCByYXcgY291bnRzIGZvciB0aGlzLiBXZSB3aWxsIHVzZSBERVNlcSBmdW5jdGlvbiB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBhbmFseXNpcyBiZXR3ZWVuIHNhbXBsZXM7IFVubGVzcyBzcGVjaWZpZWQsIHRoZSBhbmFseXNpcyBpcyBiZXR3ZWVuIHRoZSBsYXN0IGdyb3VwIGFuZCB0aGUgZmlyc3QgZ3JvdXAuIERpZmZlcmVudCBjb21wYXJpc29uIGNhbiBiZSBkb25lIHVzaW5nICdjb250cmFzdCcgYXJndW1lbnQuIFN0ZXBzIGludm9sdmVkIHVuZGVybmVhdGg6CiAgCiAgMS4gZXN0aW1hdGlvbiBvZiBzaXplIGZhY3RvcnMgKGNvbnRyb2xzIGZvciBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoIG9mIHRoZSBzYW1wbGVzKQoyLiBlc3RpbWF0aW9uIG9mIGRpc3BlcnNpb24gdmFsdWVzIGZvciBlYWNoIGdlbmUsCjMuIGZpdHRpbmcgYSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwKCiMjIyMgMS4gUnVubmluZyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcGlwZWxpbmUuIApgYGB7ciwgY2FjaGU9VFJVRX0KZGVzaWduKGRkczIpID0gfiBjZWxsICsgdHJlYXRtZW50ICsgY2VsbDp0cmVhdG1lbnQKZGRzIDwtIERFU2VxKGRkczIsIHRlc3QgPSAiTFJUIiwgcmVkdWNlZCA9IH4gY2VsbCArIHRyZWF0bWVudCkKIyBzYXZlKGRkcywgZmlsZSA9ICJERVNlcV9TQzEsNywxMF9UaW1lY291cnNlX0xSVC5SRGF0YSIpCiMgbG9hZCgiREVTZXFfU0MxLDcsMTBfVGltZWNvdXJzZV9MUlQuUkRhdGEiKQojIGRkcwpgYGAKCiMjIyMgMi4gQnVpbGRpbmcgdGhlIHJlc3VsdHMgdGFibGUuIApCeSBkZWZhdWx0LCByZXN1bHRzIHdpbGwgZXh0cmFjdCB0aGUgZXN0aW1hdGVkIGxvZzIgZm9sZCBjaGFuZ2VzIGFuZCBwIHZhbHVlcyBmb3IgdGhlIGxhc3QgdmFyaWFibGUgaW4gdGhlIGRlc2lnbiBmb3JtdWxhLiBJZiB0aGVyZSBhcmUgbW9yZSB0aGFuIDIgbGV2ZWxzIGZvciB0aGlzIHZhcmlhYmxlLCByZXN1bHRzIHdpbGwgZXh0cmFjdCB0aGUgcmVzdWx0cyB0YWJsZSBmb3IgYSBjb21wYXJpc29uIG9mIHRoZSBsYXN0IGxldmVsIG92ZXIgdGhlIGZpcnN0IGxldmVsLiAKYGBge3J9CiMgRXNpbWF0ZSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBncm91cHMgYnk6ICMgYSkgTG93ZXJpbmcgdGhlIEZEUiAocGFkaikgb3IgKGIpIHJhaXNlIHRoZSBsb2cyIGZvbGQgY2hhbmdlLgoKcmVzdWx0c05hbWVzKGRkcykKIyBhbHBoYSA9IEZEUiBhZGp1c3RlZCBwIHZhbHVlIGN1dG9mZgpyZXMgPC0gcmVzdWx0cyhkZHMsIGFscGhhID0gMC4wMDEpCnN1bW1hcnkocmVzKQpyZXNPcmRlcmVkIDwtIHJlc1tvcmRlcihyZXMkcHZhbHVlKSxdCnJkYXRhID0gYXMuZGF0YS5mcmFtZShyZXMpCmBgYAojIyMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb246IGRheXMgMCB0byA4CkNoYW5nZSBzaWduaWZpY2FudCBsb2cyIGZvbGQgY2hhbmdlIHRvIDEuNTg1ICg9PSAzLWZvbGQgY2hhbmdlIGluIGxvZzIgc3BhY2UpLgpgYGB7cn0KcmVzXzB0bzhkIDwtIHJlc3VsdHMoZGRzLCBuYW1lPSJ0cmVhdG1lbnRfOF92c18wIiwgY29va3NDdXRvZmYgPSAwLjk5LCAKICAgICAgICAgICAgICAgICAgICAgaW5kZXBlbmRlbnRGaWx0ZXJpbmcgPSBUUlVFLCBhbHBoYSA9IDAuMDUsIHBBZGp1c3RNZXRob2QgPSAiQkgiKQpzdW1tYXJ5KHJlc18wdG84ZCkKIyBvcmRlciByZXN1bHRzIHRhYmxlIGJ5IHRoZSBzbWFsbGVzdCBhZGp1c3RlZCBwIHZhbHVlOgpyZXNfMHRvOGQgPC0gcmVzXzB0bzhkW29yZGVyKHJlc18wdG84ZCRwYWRqKSxdCnJlc3VsdHNfMHRvOGQgPC0gYXMuZGF0YS5mcmFtZShyZXNfMHRvOGQpCgpyZXN1bHRzXzB0bzhkIDwtIG11dGF0ZShyZXN1bHRzXzB0bzhkLCBzaWc9aWZlbHNlKHJlc3VsdHNfMHRvOGQkcGFkajwwLjA1ICYgcmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSA+IDEuNTg1LCAiVXByZWd1bGF0ZWQiLCBpZmVsc2UocmVzdWx0c18wdG84ZCRwYWRqPDAuMDUgJiByZXN1bHRzXzB0bzhkJGxvZzJGb2xkQ2hhbmdlIDwgLTEuNTg1LCAiRG93bnJlZ3VsYXRlZCIsICJOb3QgU2lnbmlmaWNhbnQiKSkpCgpyb3cubmFtZXMocmVzdWx0c18wdG84ZCkgPC0gcm93Lm5hbWVzKHJlc18wdG84ZCkKCgpoZWFkKHJlc3VsdHNfMHRvOGQpCkRFZ2VuZXNfMHRvOGQgPC0gcmVzdWx0c18wdG84ZFt3aGljaChhYnMocmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSkgPiBsb2cyKDEuNSkgJiByZXN1bHRzXzB0bzhkJHBhZGogPCAwLjA1KSxdCgppZihTQVZFRklMRVMpIHdyaXRlLmNzdihERWdlbmVzXzB0bzhkLCBmaWxlPSJ+L0Rlc2t0b3AvREVnZW5lc18wdG84ZC5jc3YiKQpgYGAKCiMjIyBWb2xjYW5vIHBsb3QKYGBge3J9CnZvbGNhbm8gPC0gZ2dwbG90KHJlc3VsdHNfMHRvOGQsIGFlcyhsb2cyRm9sZENoYW5nZSwgLWxvZzEwKHB2YWx1ZSkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sID0gc2lnKSkgKyB0aGVtZV9idygpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImdyZXkiLCAiZ3JlZW4zIikpICsKICAjIGdndGl0bGUoIlZvbGNhbm8gUGxvdCBvZiBVbnRyZWF0ZWQgdnMgSWRsaW5nIikgKwogIGxhYnMoeD0ibG9nMihGb2xkIENoYW5nZSkiLCB5PSJMb2coT2RkcyBSYXRpbykiKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnZvbGNhbm8KIyB2b2xjYW5vICsgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGE9cmVzdWx0c18wdG84ZFsxOjEwLCBdLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmFlcyhsYWJlbD1yb3duYW1lcyhyZXN1bHRzXzB0bzhkWzE6MTAsIF0pKSkKIyBzYXZlKHJlc3VsdHNfMHRvOGQsIGZpbGU9InVudHJlYXRlZElkbGluZ19ERUEuUkRhdGEiKQpgYGAKCgpgYGB7cn0KREVnZW5lc18wdG84ZCA8LSBERWdlbmVzXzB0bzhkW29yZGVyKGFicyhERWdlbmVzXzB0bzhkJGxvZzJGb2xkQ2hhbmdlKSxERWdlbmVzXzB0bzhkJHNpZywgZGVjcmVhc2luZyA9IFRSVUUpLF0KdGVtcCA8LSBERWdlbmVzXzB0bzhkW0RFZ2VuZXNfMHRvOGQkYmFzZU1lYW4gPiAzMDAsXQp0ZW1wIDwtIHRlbXBbYWJzKHRlbXAkbG9nMkZvbGRDaGFuZ2UpPjIsXQppZihTQVZFRklMRVMpIHdyaXRlLmNzdihERWdlbmVzXzB0bzhkLCBmaWxlID0gIkRFZ2VuZXNfMHRvOGQuY3N2IikKYGBgCgoKIyBHZW5lcmF0aW5nIElvbiBDaGFubmVsIFNwZWNpZmljIEdlbmUgRGF0YWZyYW1lcwpgYGB7cn0KdGVzdCA8LSBhc3NheShkZHMpCnR5cGVzIDwtIGMoIkFUUCIsICJUUlAiLCAiR0FCUiIsICJDUkFDUiIsICJTTEMiLCAiS0NOIiwgIkNBQ04iLCAiR1JJIiwgIkFCQyIsICJTQ04iLCAiVFJQIiwgIlJJQzMiLCAiQ0hSTkQiLCAiUllSIikKc2FtcGxlcyA8LSBjKCJTQzAxX2RheTAiLCAiU0MwMV9kYXkzIiwgIlNDMDFfZGF5OCIsICJTQzA3X2RheTAiLCAiU0MwN19kYXkzIiwgIlNDMDdfZGF5OCIsICJTQzEwX2RheTAiLCAiU0MxMF9kYXkzIiwgIlNDMTBfZGF5OCIpCnRlc3QgPC0gdGVzdFtncmVwKHBhc3RlKHR5cGVzLCBjb2xsYXBzZT0ifCIpLCByb3duYW1lcyh0ZXN0KSksXQp0ZXN0MSA8LSBzYXBwbHkoc2FtcGxlcywgZnVuY3Rpb24oeCkgcm93TWVhbnModGVzdFssIGdyZXAoeCwgY29sbmFtZXModGVzdCkpXSkpCnJvd25hbWVzKHRlc3QxKSA8LSByb3duYW1lcyh0ZXN0KQp0ZXN0MSA8LSB0ZXN0MVtvcmRlcihyb3duYW1lcyh0ZXN0MSkpLF0KdGVzdDIgPC0gYXMuZGF0YS5mcmFtZSh0ZXN0MSkKdGVzdDJbImwyRkNfU0MwMV8wdG84Il0gPC0gbG9nMih0ZXN0MlsiU0MwMV9kYXk4Il0vdGVzdDJbIlNDMDFfZGF5MCJdKQp0ZXN0MlsibDJGQ19TQzA3XzB0bzgiXSA8LSBsb2cyKHRlc3QyWyJTQzA3X2RheTgiXS90ZXN0MlsiU0MwN19kYXkwIl0pCnRlc3QyWyJsMkZDX1NDMTBfMHRvOCJdIDwtIGxvZzIodGVzdDJbIlNDMTBfZGF5OCJdL3Rlc3QyWyJTQzEwX2RheTAiXSkKdGVzdDMgPC0gc3Vic2V0KHRlc3QyLCBsMkZDX1NDMDFfMHRvOCA+IDEgJiBsMkZDX1NDMDdfMHRvOCA+IDEgJiBsMkZDX1NDMTBfMHRvOCA+IDEpCnRlc3Q0IDwtIHN1YnNldCh0ZXN0MiwgbDJGQ19TQzAxXzB0bzggPiAxIHwgbDJGQ19TQzA3XzB0bzggPiAxIHwgbDJGQ19TQzEwXzB0bzggPiAxKQojIHdyaXRlLmNzdih4ID0gdGVzdDIsIGZpbGUgPSAiYWxsX2lvbkNoYW5uZWxfRXhwcmVzc2lvbi5jc3YiKQojIHdyaXRlLmNzdih4ID0gdGVzdDMsIGZpbGUgPSAiYWxsVXByZWdfaW9uQ2hhbm5lbF9FeHByZXNzaW9uLmNzdiIpCiMgd3JpdGUuY3N2KHggPSB0ZXN0NCwgZmlsZSA9ICJhdExlYXN0T25lVXByZWdfaW9uQ2hhbm5lbF9FeHByZXNzaW9uLmNzdiIpCnRlc3Q1IDwtIGxvZzIodGVzdDRbLCAxOjldKzEpCnBoZWF0bWFwKHRlc3Q1LCBjbHVzdGVyX2NvbHMgPSBGLCBjbHVzdGVyX3Jvd3MgPSBGKQp0ZXN0NiA8LSBsb2cyKCh0ZXN0M1ssMTo5XSkrMSkKcGhlYXRtYXAodGVzdDYsIGNsdXN0ZXJfcm93cyA9IEYsIGNsdXN0ZXJfY29scyA9IEYpCnRlc3Q3IDwtIHRlc3Q1W3Jvd1N1bXModGVzdDUpPjMwLF0KcGhlYXRtYXAodGVzdDcsIGNsdXN0ZXJfcm93cyA9IEYsIGNsdXN0ZXJfY29scyA9IEYpCmBgYAoKYGBge3J9CiMgbG9hZChmaWxlPSJ1bnRyZWF0ZWRJZGxpbmdfREVBLlJEYXRhIikKCk9yZ0RCIDwtIG9yZy5Icy5lZy5kYgp1cHJlZ19nZW5lcyA8LSBzdWJzZXQocmVzdWx0c18wdG84ZCwgcGFkajwwLjA1ICYgbG9nMkZvbGRDaGFuZ2U+MikKZG93bnJlZ19nZW5lcyA8LXN1YnNldChyZXN1bHRzXzB0bzhkLCBwYWRqPDAuMDUgJiBsb2cyRm9sZENoYW5nZTwoLTIpKQoKZ2VuZUxpc3RfdXAgPC0gYXMudmVjdG9yKHVwcmVnX2dlbmVzJGxvZzJGb2xkQ2hhbmdlKQpuYW1lcyhnZW5lTGlzdF91cCkgPC0gcm93bmFtZXModXByZWdfZ2VuZXMpCmdlbmVMaXN0X2Rvd24gPC0gYXMudmVjdG9yKGRvd25yZWdfZ2VuZXMkbG9nMkZvbGRDaGFuZ2UpCm5hbWVzKGdlbmVMaXN0X2Rvd24pIDwtIHJvd25hbWVzKGRvd25yZWdfZ2VuZXMpCgpnZW5lc191cCA8LSBhcy52ZWN0b3Iocm93bmFtZXModXByZWdfZ2VuZXMpKQpnZW5lc19kb3duIDwtIGFzLnZlY3Rvcihyb3duYW1lcyhkb3ducmVnX2dlbmVzKSkKIyBuYW1lcyhnZW5lTGlzdCkgPC0gcm93bmFtZXMocmVzdWx0c18wdG84ZCkKZ2VuZXNfdXBfRU5UUkVaSUQgPC0gYml0cihnZW5lc191cCwgZnJvbVR5cGUgPSAiU1lNQk9MIiwgdG9UeXBlID0gIkVOVFJFWklEIiwgT3JnRGIgPSBPcmdEQikkRU5UUkVaSUQKZ2VuZXNfZG93bl9FTlRSRVpJRCA8LSBiaXRyKGdlbmVzX2Rvd24sIGZyb21UeXBlID0gIlNZTUJPTCIsIHRvVHlwZSA9ICJFTlRSRVpJRCIsIE9yZ0RiID0gT3JnREIpJEVOVFJFWklECgojIEdyb3VwIEdPCmdnb191cCA8LSBjbHVzdGVyUHJvZmlsZXI6Omdyb3VwR08oZ2VuZSAgICAgPSBnZW5lc191cF9FTlRSRVpJRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICA9IE9yZ0RCLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCAgICA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgPSBUUlVFKQpnZ29fdXBfZGYgPC0gYXMuZGF0YS5mcmFtZShnZ29fdXApCmdnb191cF9kZiA8LSBnZ29fdXBfZGZbb3JkZXIoLWdnb191cF9kZiRDb3VudCksXSAKCmdnb19kb3duIDwtIGNsdXN0ZXJQcm9maWxlcjo6Z3JvdXBHTyhnZW5lID0gZ2VuZXNfZG93bl9FTlRSRVpJRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICA9IE9yZ0RCLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCAgICA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgPSBUUlVFKQojIFZpZXcoYXMuZGF0YS5mcmFtZShnZ29fZG93bikpCgojIEdPIG92ZXItcmVwcmVzZW50YXRpb24gdGVzdAplZ29fZ2VuZXNVcCA8LSBjbHVzdGVyUHJvZmlsZXI6OmVucmljaEdPKGdlbmUgID0gZ2VuZXNfdXBfRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiICAgICAgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ICAgICAgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZSAgICAgID0gVFJVRSkKCiMgVmlldyhhcy5kYXRhLmZyYW1lKGVnb19nZW5lc1VwKSkKCmVnb19nZW5lc0Rvd24gPC0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTyhnZW5lICA9IGdlbmVzX2Rvd25fRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiICAgICAgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ICAgICAgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZSAgICAgID0gVFJVRSkKCiMgVmlldyhhcy5kYXRhLmZyYW1lKGVnb19nZW5lc0Rvd24pKQoKIyBra19nZW5lc1VwIDwtIGVucmljaEtFR0coZ2VuZSA9IGdlbmVzX3VwX0VOVFJFWklELAojICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICdoc2EnLAojICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiMgVmlldyhhcy5kYXRhLmZyYW1lKGtrX2dlbmVzVXApKQojIAojIGtrX2dlbmVzRG93biA8LSBlbnJpY2hLRUdHKGdlbmUgPSBnZW5lc19kb3duX0VOVFJFWklELAojICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICdoc2EnLAojICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUpCiMgVmlldyhhcy5kYXRhLmZyYW1lKGtrX2dlbmVzRG93bikpCgojIGVnb19HU0VBX3VwIDwtIGdzZUdPKGdlbmVMaXN0ID0gZ2VuZUxpc3RfdXAsCiMgICAgICAgICAgICAgICBPcmdEYiAgICAgICAgPSBPcmdEQiwKIyAgICAgICAgICAgICAgIG9udCAgICAgICAgICA9ICJCUCIsCiMgICAgICAgICAgICAgICBuUGVybSAgICAgICAgPSAxMDAwLAojICAgICAgICAgICAgICAgbWluR1NTaXplICAgID0gMTAwLAojICAgICAgICAgICAgICAgbWF4R1NTaXplICAgID0gNTAwLAojICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wNSwKIyAgICAgICAgICAgICAgIHZlcmJvc2UgICAgICA9IEZBTFNFKQoKIyBiYXJwbG90KGdnb191cCwgb3JkZXI9VCkKIyBiYXJwbG90KGdnb19kb3duKQoKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQplbnJpY2hwbG90Ojpkb3RwbG90KGVnb19nZW5lc1VwKSArIGdndGl0bGUoIkdPIE92ZXItcmVwcmVzZW50YXRpb24gVXByZWd1bGF0ZWQgR2VuZXMiKSArCiAgbGFicyh4PSJHZW5lIFJhdGlvIiwgeT0iR08gVGVybXMiKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQoKZW5yaWNocGxvdDo6ZG90cGxvdChlZ29fZ2VuZXNEb3duKSArIGdndGl0bGUoIkdPIE92ZXItcmVwcmVzZW50YXRpb24gRG93bnJlZ3VsYXRlZCBHZW5lcyIpICsKICBsYWJzKHg9IkdlbmUgUmF0aW8iLCB5PSJHTyBUZXJtcyIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0iYm9sZCIpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLCBmYWNlPSJib2xkIikpCgojIGVtYXBwbG90KGVnb19nZW5lc1VwKQojIGVtYXBwbG90KGVnb19nZW5lc0Rvd24pCmVucmljaHBsb3Q6OmNuZXRwbG90KGVnb19nZW5lc1VwLCBjYXRlZ29yeVNpemU9InB2YWx1ZSIsIGNvbG9yLnBhcmFtcyA9IGxpc3QoZm9sZENoYW5nZSA9IGdlbmVMaXN0X3VwKSkKZW5yaWNocGxvdDo6Y25ldHBsb3QoZWdvX2dlbmVzRG93biwgY2F0ZWdvcnlTaXplPSJwdmFsdWUiLCBjb2xvci5wYXJhbXMgPSBsaXN0KGZvbGRDaGFuZ2UgPSBnZW5lTGlzdF9kb3duKSkKCgplZ29fZ2VuZXNVcF9kZiA8LSBhcy5kYXRhLmZyYW1lKGVnb19nZW5lc1VwKSAKZWdvVXAgPC0gZWdvX2dlbmVzVXBfZGZbb3JkZXIoLWVnb19nZW5lc1VwX2RmJENvdW50KSxdCiMgc29ydGVkX2Vnb1VwX3RvcDEwIDwtIGhlYWQoZWdvVXAsIDEwKQplZ29VcF9nZW5lcyA8LSBzdHJzcGxpdChlZ29VcCRnZW5lSUQsICIvIiwgZml4ZWQ9VFJVRSkKIyBlZ29VcF90b3AxMF9nZW5lc19hbGwgPC0gdW5saXN0KHN0cnNwbGl0KGhlYWQoZWdvVXAsIDEwKSRnZW5lSUQsICJbL10iKSkKIyBlZ29VcF90b3AxMF9nZW5lc19ncm91cCA8LSBzdHJzcGxpdChzb3J0ZWRfZWdvVXBfdG9wMTAkZ2VuZUlELCAiWy9dIikKIyBlZ29VcF90b3AxMF9nZW5lc191bmlxdWUgPC0gdW5pcXVlKGVnb1VwX3RvcDEwX2dlbmVzKQojIHRhYmxlKGVnb1VwX3RvcDEwX2dlbmVzKQojIGVnb1VwX2dlbmVzQnlHcm91cCA8LSBhcy5kYXRhLmZyYW1lKHQocGx5cjo6bGRwbHkoZWdvVXBfdG9wMTBfZ2VuZXNfZ3JvdXAsIHJiaW5kKSkpCiMgY29sbmFtZXMoZWdvVXBfZ2VuZXNCeUdyb3VwKSA8LSBzb3J0ZWRfZWdvVXBfdG9wMTAkRGVzY3JpcHRpb24KIyBlZ29VcF9nZW5lc0J5R3JvdXBfaW9uT25seSA8LSBlZ29VcF9nZW5lc0J5R3JvdXBbLGMoMTo2LDg6MTApXQoKIyB3cml0ZS5jc3YoZWdvVXBfZ2VuZXNCeUdyb3VwLCBmaWxlPSJ0b3AxMEdPdGVybXNVcHJlZ3VsYXRlZF9nZW5lTWVtYmVyc2hpcC5jc3YiKQojIGlvbkdlbmVzIDwtIHVuaXF1ZSh1bmxpc3QoZWdvVXBfZ2VuZXNCeUdyb3VwX2lvbk9ubHkpKQojIAojIGVuc2VtYmwgPSB1c2VFbnNlbWJsKGJpb21hcnQ9ImVuc2VtYmwiLCBkYXRhc2V0PSJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiKQojIElEcyA8LSBhcy5jaGFyYWN0ZXIoaW9uR2VuZXMpCiMgZ2VuZVVwSUQgPC0gbmFtZXMoZ2VuZUxpc3RfdXApCiMgZ2VuZURvd25JRCA8LSBuYW1lcyhnZW5lTGlzdF9kb3duKQojIGdlbmVkZXNjX2lvbiA8LSBnZXRCTShhdHRyaWJ1dGVzPWMoJ2V4dGVybmFsX2dlbmVfbmFtZScsJ2Rlc2NyaXB0aW9uJyksIGZpbHRlcnMgPSAnZXh0ZXJuYWxfZ2VuZV9uYW1lJywgdmFsdWVzID0gSURzLCBtYXJ0ID1lbnNlbWJsKQojIHdyaXRlLmNzdihnZW5lZGVzY19pb24sIGZpbGUgPSAiaW9uQ2hhbm5lbEdlbmVzX2Rlc2NyaXB0aW9uLmNzdiIpCgojIGdlbmVkZXNjX1VwIDwtIGdldEJNKGF0dHJpYnV0ZXM9YygnZXh0ZXJuYWxfZ2VuZV9uYW1lJywnZGVzY3JpcHRpb24nKSwgZmlsdGVycyA9ICdleHRlcm5hbF9nZW5lX25hbWUnLCB2YWx1ZXMgPSBnZW5lVXBJRCwgbWFydCA9ZW5zZW1ibCkKIyB3cml0ZS5jc3YoZ2VuZWRlc2NfVXAsIGZpbGUgPSAidXByZWd1bGF0ZWRHZW5lc19kZXNjcmlwdGlvbi5jc3YiKQojIGdlbmVkZXNjX0Rvd24gPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCdleHRlcm5hbF9nZW5lX25hbWUnLCdkZXNjcmlwdGlvbicpLCBmaWx0ZXJzID0gJ2V4dGVybmFsX2dlbmVfbmFtZScsIHZhbHVlcyA9IGdlbmVEb3duSUQsIG1hcnQgPWVuc2VtYmwpCiMgd3JpdGUuY3N2KGdlbmVkZXNjX0Rvd24sIGZpbGUgPSAiZG93bnJndWxhdGVkR2VuZXNfZGVzY3JpcHRpb24uY3N2IikKYGBgCgoKYGBge3J9CmdlbmVMaXN0X2FsbCA8LSBhcy52ZWN0b3IocmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSkKbmFtZXMoZ2VuZUxpc3RfYWxsKSA8LSByb3duYW1lcyhyZXN1bHRzXzB0bzhkKQphIDwtIG5hbWVzKGdlbmVMaXN0X2FsbCkKZ2VuZXNfRU5UUkVaSUQgPC0gYml0cihhLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IE9yZ0RCKSRFTlRSRVpJRApuYW1lcyhnZW5lTGlzdF9hbGwpIDwtIGdlbmVzX0VOVFJFWklECgpnZW5lX2RmIDwtIGRhdGEuZnJhbWUoRW50cmV6PW5hbWVzKGdlbmVMaXN0X2FsbCksIEhHTkM9YSwgRkM9Z2VuZUxpc3RfYWxsKQpnZW5lX2RmIDwtIGdlbmVfZGZbYWJzKGdlbmVfZGYkRkMpID4gMSxdCmdlbmVfZGYkZ3JvdXAgPC0gInVwcmVndWxhdGVkIgpnZW5lX2RmJGdyb3VwW2dlbmVfZGYkRkMgPCAwXSA8LSAiZG93bnJlZ3VsYXRlZCIKZ2VuZV9kZiRvdGhlcmdyb3VwIDwtICJBIgpnZW5lX2RmJG90aGVyZ3JvdXBbYWJzKGdlbmVfZGYkRkMpID4gMl0gPC0gIkIiCgpmb3JtdWxhX3JlcyA8LSBjb21wYXJlQ2x1c3RlcihFbnRyZXp+Z3JvdXArb3RoZXJncm91cCwgZGF0YT1nZW5lX2RmLCBmdW49ImVucmljaEtFR0ciKQpoZWFkKGFzLmRhdGEuZnJhbWUoZm9ybXVsYV9yZXMpKQoKYGBgCiMjIyMgMy4gRXhwbG9yaW5nIFJlc3VsdHMKCmBgYHtyfQpwbG90TUEocmVzLCB5bGltPWMoLTIsMikpCnBsb3RDb3VudHMoZGRzLCBnZW5lPXdoaWNoLm1pbihyZXMkcGFkaiksIGludGdyb3VwPSJ0cmVhdG1lbnQiKQoKYGBgCgojIyMjIExvZyBub3JtYWxpemUgcmVzdWx0cyAjIyMjCmBgYHtyfQoKIyBub3JtYWxpemVkQ291bnRzIDwtIHQoIHQoY291bnRzKGRkcykpIC8gc2l6ZUZhY3RvcnMoZGRzKSApCgojbG9nMiBub3JtYWxpemVkIGNvdW50cwpybGQyIDwtIHJsb2coZGRzLCBibGluZCA9IEZBTFNFKQojIHNhdmUocmxkMiwgZmlsZSA9ICJSTEQyX1NDMSw3LDEwX1RpbWVjb3Vyc2VfaG1hcC5SRGF0YSIpCgojIGxvYWQoIlJMRDJfU0MxLDcsMTBfVGltZWNvdXJzZV9obWFwLlJEYXRhIikKYGBgCgojIyMjIENsdXN0ZXJpbmcgIyMjCgpgYGB7cn0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHJsZDIpKSkKc2FtcGxlRGlzdHMKCnNhbXBsZURpc3RNYXRyaXggPC0gYXMubWF0cml4KCBzYW1wbGVEaXN0cyApCnJvd25hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIHBhc3RlKHJsZDIkdHJlYXRtZW50LCBybGQyJGNlbGwsIHNlcCA9ICIgLSAiICkKY29sbmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gTlVMTApjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQpwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBzYW1wbGVEaXN0cywKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gc2FtcGxlRGlzdHMsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCgpwb2lzZCA8LSBQb2lDbGFDbHU6OlBvaXNzb25EaXN0YW5jZSh0KGNvdW50cyhkZHMpKSkKc2FtcGxlUG9pc0Rpc3RNYXRyaXggPC0gYXMubWF0cml4KCBwb2lzZCRkZCApCnJvd25hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBwYXN0ZSggZGRzJGRleCwgZGRzJGNlbGwsIHNlcD0iIC0gIiApCmNvbG5hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBOVUxMCnBoZWF0bWFwKHNhbXBsZVBvaXNEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBwb2lzZCRkZCwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gcG9pc2QkZGQsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCm1kcyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMikpICAlPiUKICAgICAgICAgY2JpbmQoY21kc2NhbGUoc2FtcGxlRGlzdE1hdHJpeCkpCmdncGxvdChtZHMsIGFlcyh4ID0gYDFgLCB5ID0gYDJgLCBjb2xvciA9IGNlbGwsIHNoYXBlID0gdHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsgY29vcmRfZml4ZWQoKSArIHRoZW1lX2J3KCkgKwogIHhsYWIoIlBDMSIpICsgeWxhYigiUEMyIikgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwKICAgICAgICAjIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKIyBsaWJyYXJ5KCJnZW5lZmlsdGVyIikKdG9wVmFyR2VuZXMgPC0gaGVhZChvcmRlcihyb3dWYXJzKGFzc2F5KHJsZDIpKSwgZGVjcmVhc2luZyA9IFRSVUUpLCA1MDAwKQptYXQgIDwtIGFzc2F5KHJsZDIpWyB0b3BWYXJHZW5lcywgXQptYXQgIDwtIG1hdCAtIHJvd01lYW5zKG1hdCkKYW5ubyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMilbLCBjKCJjZWxsIiwidHJlYXRtZW50IildKQpuYW1lcyhhbm5vKSA8LSBjKCJDZWxsIiwgIlRyZWF0bWVudCIpCmFubm90YXRpb25fY29sb3JzID0gbGlzdCgKICBDZWxsID0gYyhTQzAxPSJyZWQyIiwgU0MwNz0iZ3JlZW4yIiwgU0MxMD0iYmx1ZTIiKSwKICBUcmVhdG1lbnQgPSBjKCIwIj0iY3lhbjIiLCAiMyI9ImRhcmtvcmFuZ2UiLCAiOCI9ImRhcmtvcmNoaWQiKSkKcGhlYXRtYXAobWF0LCBhbm5vdGF0aW9uX2NvbCA9IGFubm8sIHNob3dfcm93bmFtZXMgPSBGLCBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uX2NvbG9ycykKYGBgCgojIyMjIFRpbWUgc2VyaWVzIGFuYWx5c2lzICMjIyMKCiMgMSBERVNlcTIgdGltZSBzZXJpZXMgYW5hbHlzaXMKYGBge3J9CiMgYnJvd3NlVmlnbmV0dGVzKCJybmFzZXFHZW5lIikKCmRkc1RDIDwtIERFU2VxKGRkcywgdGVzdD0iTFJUIiwgcmVkdWNlZCA9IH4gY2VsbCArIHRyZWF0bWVudCkKcmVzVEMgPC0gcmVzdWx0cyhkZHNUQykKcmVzVEMkc3ltYm9sIDwtIG1jb2xzKGRkc1RDKSRzeW1ib2wKIyBoZWFkKHJlc1RDW29yZGVyKHJlc1RDJHBhZGopLF0sIDQpCgp0YyA8LSBwbG90Q291bnRzKGRkc1RDLCB3aGljaC5taW4ocmVzVEMkcGFkaiksIAogICAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSBjKCJ0cmVhdG1lbnQiLCJjZWxsIiksIHJldHVybkRhdGEgPSBUUlVFKQoKZGRzVENbd2hpY2gubWluKHJlc1RDJHBhZGopLF0KCmdncGxvdCh0YywKICBhZXMoeCA9IHJlcChjKDAsMyw4KSwgZWFjaD05KSwgeSA9IGNvdW50LCBjb2xvciA9IGNlbGwsIGdyb3VwID0gY2VsbCkpICsgCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbWV0aG9kID0gImxvZXNzIikgKyBzY2FsZV95X2xvZzEwKCkgKwogIHRoZW1lX2J3KCkgKwogIGdndGl0bGUoIlRpbWUgQ291cnNlIEV4cHJlc3Npb24gb2YgUERLNCIpICsKICBsYWJzKHg9IlRpbWUgKGRheXMpIiwgeT0iR2VuZSBDb3VudCIpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKcmVzdWx0c05hbWVzKGRkc1RDKQoKYmV0YXMgPC0gY29lZihkZHNUQykKY29sbmFtZXMoYmV0YXMpCgp0b3BHZW5lcyA8LSBoZWFkKG9yZGVyKHJlc1RDJHBhZGopLDUwKQptYXQgPC0gYmV0YXNbdG9wR2VuZXMsIC1jKDEsMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKcGhlYXRtYXAobWF0LCBicmVha3M9c2VxKGZyb209LXRociwgdG89dGhyLCBsZW5ndGg9MTAxKSwKICAgICAgICAgY2x1c3Rlcl9jb2w9RkFMU0UpCmBgYAoKCiMjIyBOT1RFCk9yaWdpbmFsIGNvZGUgYmVsb3cgcHJvZHVjZWQgbWFueSBtZXNzYWdlcyBvZiBgTm8gaWQgdmFyaWFibGVzOyB1c2luZyBhbGwgYXMgbWVhc3VyZSB2YXJpYWJsZXNgOyBwcmVzdW1hYmx5IGEgbGluZSBmb3IgZWFjaCBnZW5lLiBUaGlzIGlzIGR1ZSB0byB0aGUgYG1lbHRgIGZ1bmN0aW9uIG5vdCBoYXZpbmcgYW55IGlkIHZhcmlhYmxlcyB0byB1c2UuICAKCgpSZWppZ2dlcmluZyBjb2RlIG5vdCB5ZXQgZmluaXNoZWQuICBTaG91bGQgcHJvYmFibHkgdXNlIApgYGB7ciBTdWJsaW5lIEFOT1ZBLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFfQojIDEuMSBBTk9WQSAtIGNvbXBhcmUgYnR3biBzdWJsaW5lcyAKIyBncm91cCA8LSBhcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX2Jhc2VsaW5lIDwtIGxpc3QoKQpUdWtleVNDMDd0b1NDMDEgPC0gbGlzdCgpClR1a2V5U0MxMHRvU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzEwdG9TQzA3IDwtIGxpc3QoKQpub3JtX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShhc3NheShybGQyKSlbYygnU0MwMV9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMycpXQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIE5ldyBjb2RlIGJ5IERSVCAjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHNhbXBfbmFtZXMgPC0gY29sbmFtZXMobm9ybV9kYXRhKQoKIyBjb21wYXJlU3ViY2xvbmVzIDwtIGZ1bmN0aW9uKGdlbmVfbmFtZSwgZGF0PW5vcm1fZGF0YSwgc2FtcF9uYW1lcz1OVUxMLCBncm91cD1OVUxMKQojIHsKIyAgICAgaWYoaXMubnVsbChncm91cCkpIGdyb3VwIDwtIGFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyAgICAgaWYoaXMubnVsbChzYW1wX25hbWVzKSkgc2FtcF9uYW1lcyA8LSBjb2xuYW1lcyhkYXQpCiMgICAgICMgZGZhID0gZGF0YSBmb3IgYW5hbHlzaXMKIyAgICAgZGZhIDwtIGRhdGEuZnJhbWUodmFsdWU9YXMubnVtZXJpYyh0KGRhdFtnZW5lX25hbWUsXSkpLCBncm91cD1ncm91cCkKIyAgICAgcm93bmFtZXMoZGZhKSA8LSBzYW1wX25hbWVzCiMgICAgIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGRmYSkKIyAgICAgYW5vdmFfYmFzZWxpbmUgPC0gc3VtbWFyeShmaXQpW1sxXV1bWydQcig+RiknXV1bMV0KIyAgICAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQojICAgICBwdmFsIDwtIGRhdGEuZnJhbWUocF9hZGo9cmVzdWx0cyRncm91cFssJ3AgYWRqJ10pCiMgICAgIHJvd25hbWVzKHB2YWwpIDwtIGMoIlR1a2V5U0MwN3RvU0MwMSIsIlR1a2V5U0MxMHRvU0MwMSIsIlR1a2V5U0MxMHRvU0MwNyIpCiMgICAgIG91dCA8LSBsaXN0KGFub3ZhX2Jhc2VsaW5lID0gYW5vdmFfYmFzZWxpbmUsCiMgICAgICAgICAgICAgICAgIHB2YWwgPSBwdmFsKQojICAgICAjIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQojICAgICAjIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQojICAgICAjIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXSAgICAKIyAgICAgcmV0dXJuKG91dCkKIyB9CgojIHRlbXAgPC0gbGFwcGx5KHJvd25hbWVzKG5vcm1fZGF0YSksIGNvbXBhcmVTdWJjbG9uZXMpCiMgYW5vdmFfcHZhbCA8LSBzYXBwbHkodGVtcCwgIltbIiwgMSkKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMgRW5kIG5ldyBjb2RlICMjIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpmb3IgKGdlbmUgaW4gMTpucm93KG5vcm1fZGF0YSkpIHsKICBnZW5lX25vcm1fZGF0YSA8LSBub3JtX2RhdGFbZ2VuZSxdCiAgIyBkMyA8LSBkYXRhLmZyYW1lKHkgPSBnZW5lX25vcm1fZGF0YSwgZ3JvdXAgPSBncm91cCkKICAjIGZpdCA8LSBsbSh5fmdyb3VwLCBkMykKICBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIGRhdGEuZnJhbWUodmFyaWFibGU9Y29sbmFtZXMoZ2VuZV9ub3JtX2RhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZT1hcy5udW1lcmljKHQoZ2VuZV9ub3JtX2RhdGEpKSkKICBnZW5lX25vcm1fZGF0YV9tZWx0JGdyb3VwIDwtIGdyb3VwCiAgZml0IDwtIGFvdih2YWx1ZX5ncm91cCwgZ2VuZV9ub3JtX2RhdGFfbWVsdCkKICAjIGFub3ZhX2xpc3RbZ2VuZV0gPC0gYW5vdmEoZml0KSQnUHIoPkYpJ1sxXQogIGFub3ZhX2Jhc2VsaW5lW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKCiAgCmFub3ZhX3B2YWwgPC0gdW5saXN0KGFub3ZhX2Jhc2VsaW5lKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzA3dG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MwN3RvU0MwMSkKVHVrZXlTQzEwdG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwMSkKVHVrZXlTQzEwdG9TQzA3X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwNykKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBhbm92YV9wdmFsKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBUdWtleVNDMDd0b1NDMDFfcHZhbCkKbm9ybV9kYXRhX3N0YXRzIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0cywgVHVrZXlTQzEwdG9TQzAxX3B2YWwpCm5vcm1fZGF0YV9zdGF0cyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHMsIFR1a2V5U0MxMHRvU0MwN19wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0cywgZmlsZSA9ICJzdWJjbG9uZUNvdW50c19hbm92YV90dWtleV9ERVNlcTIuUkRhdGEiKQoKIyBJZGVudGlmeSBnZW5lcyB0aGF0IGRpZmZlciBiZXR3ZWVuIGNsb25lcyBiYXNlZCBvbiAKIyBBTk9WQSBwLXZhbHVlIHdpdGggZGVmaW5lZCB0aHJlc2hvbGQKc2lnVGhyZXNoIDwtIDAuMDUKdGFibGUoYW5vdmFfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3dG9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMHRvU0MwMV9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTB0b1NDMDdfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c1siYW5vdmFfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGwgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzWyJhbm92YV9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNbIlR1a2V5U0MwN3RvU0MwMV9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c1siVHVrZXlTQzEwdG9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzWyJUdWtleVNDMTB0b1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c1tzaWdJbmRlY2llcyxdKQpzaWdEaWZmR2VuZXNBbGwgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzW3NpZ0luZGVjaWVzQWxsLF0pCmBgYAoKIyAyLiBBTk9WQSBidHduIHRpbWUgcG9pbnRzICYgc2hhcmVkIGJ0d24gc3VibGluZXMpCmBgYHtyIFNDMDEgdGltZSBBTk9WQSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzAxX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMDFfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MwMV90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMDF0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMDFfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MwMXRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMDF0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgIyBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIG1lbHQoZ2VuZV9ub3JtX2RhdGEpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCgogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwMVtnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDFfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDFfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDFfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDFfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwMSkgIyBtYWtlIGFycmF5ClR1a2V5U0MwMV90aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTApClR1a2V5U0MwMV90aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTMpClR1a2V5U0MwMV90aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDF0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgYW5vdmFfU0MwMV9wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgVHVrZXlTQzAxX3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBUdWtleVNDMDFfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDEgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDEsIFR1a2V5U0MwMV90aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzAxdGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwMV90aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDFfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzAxX3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzAxIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzAxWyJhbm92YV9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDEgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDFbImFub3ZhX1NDMDFfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDFbIlR1a2V5U0MwMV90aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzAxWyJUdWtleVNDMDFfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwMVsiVHVrZXlTQzAxX3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzAxIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzAxW3NpZ0luZGVjaWVzX1NDMDEsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDEgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDFbc2lnSW5kZWNpZXNBbGxfU0MwMSxdKQpgYGAKCgpgYGB7ciBTQzA3IHRpbWUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9Cmdyb3VwPC1hcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX1NDMDcgPC0gbGlzdCgpClR1a2V5U0MwN190aW1lMCA8LSBsaXN0KCkKVHVrZXlTQzA3X3RpbWUzIDwtIGxpc3QoKQpUdWtleVNDMDdfdGltZTggPC0gbGlzdCgpCm5vcm1fZGF0YV9TQzA3dGltZSA8LSBhcy5kYXRhLmZyYW1lKGFzc2F5KHJsZDIpKVtjKCdTQzA3X2RheTBfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTNfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkzX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5M19yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheThfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXk4X3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5OF9yZXAzJyldCmZvciAoZ2VuZSBpbiAxOm5yb3cobm9ybV9kYXRhX1NDMDd0aW1lKSkgewogIGdlbmVfbm9ybV9kYXRhIDwtIG5vcm1fZGF0YV9TQzA3dGltZVtnZW5lLF0KICAjIGQzIDwtIGRhdGEuZnJhbWUoeSA9IGdlbmVfbm9ybV9kYXRhLCBncm91cCA9IGdyb3VwKQogICMgZml0IDwtIGxtKHl+Z3JvdXAsIGQzKQogICMgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBtZWx0KGdlbmVfbm9ybV9kYXRhKQogIGdlbmVfbm9ybV9kYXRhX21lbHQgPC0gZGF0YS5mcmFtZSh2YXJpYWJsZT1jb2xuYW1lcyhnZW5lX25vcm1fZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlPWFzLm51bWVyaWModChnZW5lX25vcm1fZGF0YSkpKQogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwN1tnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDdfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDdfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDdfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDdfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwNykgIyBtYWtlIGFycmF5ClR1a2V5U0MwN190aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTApClR1a2V5U0MwN190aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTMpClR1a2V5U0MwN190aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDd0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgYW5vdmFfU0MwN19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgVHVrZXlTQzA3X3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBUdWtleVNDMDdfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDcgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDcsIFR1a2V5U0MwN190aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzA3dGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzA3X3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwN190aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDdfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3X3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzA3IDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzA3WyJhbm92YV9TQzA3X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDcgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDdbImFub3ZhX1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDdbIlR1a2V5U0MwN190aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzA3WyJUdWtleVNDMDdfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwN1siVHVrZXlTQzA3X3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzA3IDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzA3W3NpZ0luZGVjaWVzX1NDMDcsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDcgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDdbc2lnSW5kZWNpZXNBbGxfU0MwNyxdKQpgYGAKCmBgYHtyIFNDMTAgdGltZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MxMCA8LSBsaXN0KCkKVHVrZXlTQzEwX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMTBfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MxMF90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMTB0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMTBfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MxMHRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMTB0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCRncm91cCA8LSBncm91cAogIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGdlbmVfbm9ybV9kYXRhX21lbHQpCiAgIyBhbm92YV9saXN0W2dlbmVdIDwtIGFub3ZhKGZpdCkkJ1ByKD5GKSdbMV0KICBhbm92YV9TQzEwW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MxMF90aW1lMFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMF90aW1lM1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMF90aW1lOFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQogIAp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKYW5vdmFfU0MxMF9wdmFsIDwtIHVubGlzdChhbm92YV9TQzEwKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzEwX3RpbWUwX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMCkKVHVrZXlTQzEwX3RpbWUzX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMykKVHVrZXlTQzEwX3RpbWU4X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lOCkKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gYXMuZGF0YS5mcmFtZShub3JtX2RhdGFfU0MxMHRpbWUpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBhbm92YV9TQzEwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBUdWtleVNDMTBfdGltZTBfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMTAsIFR1a2V5U0MxMF90aW1lM19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MxMCA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MxMCwgVHVrZXlTQzEwX3RpbWU4X3B2YWwpCgojIHNhdmUobm9ybV9kYXRhX3N0YXRzX1NDMTAsIGZpbGUgPSAic3ViY2xvbmVDb3VudHNfYW5vdmFfdHVrZXlfREVTZXEyX1NDMTB0aW1lLlJEYXRhIikKCiMgSWRlbnRpZnkgZ2VuZXMgdGhhdCBkaWZmZXIgYmV0d2VlbiBjbG9uZXMgYmFzZWQgb24gCiMgQU5PVkEgcC12YWx1ZSB3aXRoIGRlZmluZWQgdGhyZXNob2xkCnNpZ1RocmVzaCA8LSAwLjA1CnRhYmxlKGFub3ZhX1NDMTBfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzEwX3RpbWUwX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMF90aW1lM19wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTBfdGltZThfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzX1NDMTAgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMTBbImFub3ZhX1NDMTBfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGxfU0MxMCA8LSB3aGljaChub3JtX2RhdGFfc3RhdHNfU0MxMFsiYW5vdmFfU0MxMF9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MxMFsiVHVrZXlTQzEwX3RpbWUwX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMTBbIlR1a2V5U0MxMF90aW1lM19wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzEwWyJUdWtleVNDMTBfdGltZThfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzX1NDMTAgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMTBbc2lnSW5kZWNpZXNfU0MxMCxdKQpzaWdEaWZmR2VuZXNBbGxfU0MxMCA8LSByb3duYW1lcyhub3JtX2RhdGFfc3RhdHNfU0MxMFtzaWdJbmRlY2llc0FsbF9TQzEwLF0pCmBgYAoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCmFsbF9TQ3NfdGltZSA8LSBSZWR1Y2UoaW50ZXJzZWN0LCAKICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHNpZ0RpZmZHZW5lc0FsbF9TQzAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnRGlmZkdlbmVzQWxsX1NDMDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdEaWZmR2VuZXNBbGxfU0MxMCkpCgpkZl9hbGxTQ3NfdGltZSA8LSBkYXRhLmZyYW1lKGdlbmUgPSBhbGxfU0NzX3RpbWUpCiMgZ2VuZXMgPC0gZGZfYWxsU0NzX3RpbWUkZ2VuZQojIEdfbGlzdCA8LSBnZXRCTShmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwgYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSx2YWx1ZXM9Z2VuZXMsbWFydD1tYXJ0KQptZXJnZShkZl9hbGxTQ3NfdGltZSxHX2xpc3QsYnkueD0iZ2VuZSIsYnkueT0iZW5zZW1ibF9nZW5lX2lkIikKCiMgd3JpdGUuY3N2KEdfbGlzdCwgZmlsZSA9ICJBTk9WQV9hbGxTQ3NUaW1lX3NoYXJlZF9nZW5lcy5jc3YiKQoKIyBDb21wYXJlIGdlbmUgbGlzdHMgZm9yIGJldHdlZW4gc3VibGluZXMgYW5kIHRpbWUKIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKZGJzIDwtIGxpc3RFbnJpY2hyRGJzKCkKZGJzIDwtIGMoIktFR0dfMjAxNiIsIAogICAgICAgICAiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTgiKQoKZW5yaWNoZWRfYWxsU0NzdGltZSA8LSBlbnJpY2hyKEdfbGlzdCRoZ25jX3N5bWJvbCwgZGJzKQoKS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUgPC0gZW5yaWNoZWRfYWxsU0NzdGltZVtbIktFR0dfMjAxNiJdXVsxOjUsXQpLRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtcyA8LSBmYWN0b3IoS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUkVGVybSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1LRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtKQoKZ2dwbG90KEtFR0dfdXByZWdfYWxsU0NzdGltZV90b3A1LCAKICAgICAgIGFlcyh4PVRlcm1zLCB5PS1sb2cxMChBZGp1c3RlZC5QLnZhbHVlKSkpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeCA9ICJQYXRod2F5IFRlcm0iLCB5ID0gIi1sb2cxMChxLXZhbHVlKSIpICArCiAgdGhlbWVfYncoKSAgKyBnZ3RpdGxlKCJQYXRod2F5IEVucmljaG1lbnQgLSBLRUdHIDIwMTYiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNCxmYWNlPSJib2xkIikpCiAgCgpgYGAKCiMgMyBKYWNrJ3MgbWV0aG9kIApgYGB7ciBldmFsPUZBTFNFfQojR3JhYiBhbGwgdGhlIG5hbWVzIGZyb20gcmVzIGluIHRoZSBERVNlcSBtYXRyaXgKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCgpjb3VudE1BVCA8LSBkYXRhLmZyYW1lKG5vcm1hbGl6ZWRDb3VudHNbdG9wR2VuZXMsXSkKCnN1YnJsID0gZGF0YS5mcmFtZShhc3NheShybGQyKSkKcmxNQVQgPSBkYXRhLmZyYW1lKHN1YnJsW3RvcEdlbmVzLF0pCgojTGFiZWxpbmcgcm93cyB3aXRoIEVOU0cgSURzCiMgY291bnRNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKGNvdW50TUFUKQojIGNvdW50TUFUJHBhZGogPSByZXNbdG9wR2VuZXMsInBhZGoiXQoKcmxNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKHJsTUFUKQpybE1BVCRwYWRqID0gcmVzW3RvcEdlbmVzLCJwYWRqIl0KCiMgbGlicmFyeShiaW9tYVJ0KQojIGVuc2VtYmwgPC0gdXNlTWFydCgiZW5zZW1ibCIpCiMgbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKIyBnZW5lcyA9IHJvdy5uYW1lcyhybE1BVCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKI0NoZWNrIGlmIGRhdGEgZml0cyBhIG5vcm1hbCBkaXN0cmlidXRpb24KIyBwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgoY291bnRNQVRbLDE6MjddKSkpKQpwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgocmxNQVRbLDE6MjddKSkpKQoKCiNybE1BVCBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgdGhlcmVmb3JlIHdlIHdpbGwgdXNlIHRoaXMgaW4gdGhlIGhlYXRtYXAgY29uc3RydWN0aW9uCiNMYWJlbGluZyBkZiB3aXRoIGhnbmMgc3ltYm9scwpHRV9kYXRhIDwtIG1lcmdlKEdfbGlzdCwgcmxNQVQsIGJ5ID0gImVuc2VtYmxfZ2VuZV9pZCIpCgojTWFraW5nIHJvd25hbWVzIHVuaXF1ZSBoZ25jIHN5bWJvbHMKcm93bmFtZXMoR0VfZGF0YSkgPC0gbWFrZS5uYW1lcyhHRV9kYXRhWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI0F2ZXJhZ2luZyBybGQgYmV0d2VlbiB0cmlhbHMKQWNvbCA8LSBjKCJTQzAxX2RheTAiLAogICAgICAgICAgIlNDMDFfZGF5MyIsCiAgICAgICAgICAiU0MwMV9kYXk4IiwKICAgICAgICAgICJTQzA3X2RheTAiLAogICAgICAgICAgIlNDMDdfZGF5MyIsCiAgICAgICAgICAiU0MwN19kYXk4IiwKICAgICAgICAgICJTQzEwX2RheTAiLAogICAgICAgICAgIlNDMTBfZGF5MyIsCiAgICAgICAgICAiU0MxMF9kYXk4IikKZm9yKGkgaW4gMTpsZW5ndGgoQWNvbCkpewogIGogPSAyK2kKICBrID0gMiszKmkKICBHRV9kYXRhWyxBY29sW2ldXSA9IHJvd01lYW5zKEdFX2RhdGFbLGMoajprKV0pCn0KCgojQ2FsY3VsYXRpbmcgZm9sZCBjaGFuZ2VzIGFjcm9zcyBjb25kaXRpb25zIGluIGEgdHJpYW5ndWxhciBtYXRyaXggZm9ybQpHRV9tZWFuID0gR0VfZGF0YVssYygxLDIsMzA6MzkpXQpERVByb2MgPSBHRV9tZWFuCnN0YXJ0Y29sID0gNAplbmRjb2wgPSAxMgoKYWxsRkMgPC0gZnVuY3Rpb24oREVQcm9jLHN0YXJ0Y29sLGVuZGNvbCl7IAogIEdFX2ZvbGQgPSBERVByb2NbLC1jKHN0YXJ0Y29sOmVuZGNvbCldCiAgY29sdmVjID0gY29sbmFtZXMoREVQcm9jKVtzdGFydGNvbDplbmRjb2xdCiAgCiAgI0xhc3QgaW5kZXggaXMgYSBzZWxmIGNvbXBhcmlzb24gYW5kIGlzIHJlbW92ZWQKICBmb3IoayBpbiAxOihsZW5ndGgoY29sdmVjKS0xKSl7CiAgICAjU3RhcnQgd2l0aCBjb2x1bW4gdGhhdCBpcyAxIGF3YXkgZnJvbSBpbmRleCAKICAgIGZvcihqIGluIChrKzEpOmxlbmd0aChjb2x2ZWMpKXsKICAgICAgY29tcG5hbSA9IHBhc3RlMChjb2x2ZWNbal0sIi8iLGNvbHZlY1trXSkKICAgICAgI0xvb3AgdGhyb3VnaCBlYWNoIGdlbmUvcm93ICAKICAgICAgZm9yKGkgaW4gMTpucm93KERFUHJvYykpewogICAgICAgIGYgPSBERVByb2NbaSxjb2x2ZWNbal1dCiAgICAgICAgaCA9IERFUHJvY1tpLGNvbHZlY1trXV0KICAgICAgICAKICAgICAgICAjQ2FwdHVyZSB1cHJlZ3VsYXRpb24gYW5kIGRvd24gcmVndWxhdGlvbgogICAgICAgIGlmKGY+aCl7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAyXihmLWgpCiAgICAgICAgfWVsc2V7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAtMl4oaC1mKQogICAgICAgIH0KICAgICAgICAKICAgICAgfQogICAgfQogIH0KICAKICByZXR1cm4oR0VfZm9sZCkKICAKfQoKI1N1YnNldCBnZW5lLCB0aGVuIHBsb3QsIHRoZW4gc2F2ZSBwbG90CiNQZXJoYXBzIG1ha2UgaGVhdG1hcHMgd2l0aCBzY2FsZWQgeiBzY29yZXMKI0lzIHRoZXJlIGEgd2F5IHRvIGNvbnNvbGlkYXRlIHJlcGxpY2F0ZSB6IHNjb3Jlcz8gR2VvbWV0cmljIG1lYW4/IAojUmVndWxhciBtZWFuLCB0aGVuIHNjYWxlLgoKIyBJbXBSYXQgPSBjb2xuYW1lcyhHRV9mb2xkKVtjKDQsNSw2LDksMTIsMTQsMTcsMjEsMjQsMjUsMjYsMjcsMzAsMzIsMzYsMzcsMzgsMzkpXQoKI0xpc3Rpbmcgb2YgYWxsIGltcG9ydGFudCBjb21wYXJpc29ucz8KSW1wUmF0ID0gYygiU0MwMV9kYXkzL1NDMDFfZGF5MCIsICJTQzAxX2RheTgvU0MwMV9kYXkzIiwgIlNDMDFfZGF5OC9TQzAxX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkzL1NDMDdfZGF5MCIsICJTQzA3X2RheTgvU0MwN19kYXkzIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAKICAgICAgICAgICAiU0MxMF9kYXkzL1NDMTBfZGF5MCIsICJTQzEwX2RheTgvU0MxMF9kYXkzIiwgIlNDMTBfZGF5OC9TQzEwX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiLAogICAgICAgICAgICJTQzA3X2RheTMvU0MwMV9kYXkzIiwgIlNDMTBfZGF5My9TQzAxX2RheTMiLCAiU0MxMF9kYXkzL1NDMDdfZGF5MyIsCiAgICAgICAgICAgIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IiApCkltcF9mb2xkID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBJbXBSYXQpXQpJbXBfZm9sZDIgPSBJbXBfZm9sZFtyb3dTdW1zKGFicyhJbXBfZm9sZFssNDoyMV0pPj0xLjUpPj0xLF0KCiMgd3JpdGUudGFibGUoSW1wX2ZvbGQsIlNDMSw3LDEwLVRpbWVjb3Vyc2VQTFgtSW1wb3J0YW50RkNfMjAxODA3MjIudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GKQoKSW1wX2ZvbGQgPSByZWFkLmRlbGltKCJTQzEsNywxMC1UaW1lY291cnNlUExYLUltcG9ydGFudEZDXzIwMTgwNzIyLnR4dCIsIHNlcD0iXHQiKQoKI1N1YnNldCB0aGUgTEYgbWVhbiBvZiBpbXBvcnRhbnQgZ2VuZXMgZnJvbSBMb2cyIEZvbGQgQ2hhbmdlIChMRkMpIGNvbXBhcmlzb24gZGF0YSBmcmFtZS4KR0VfSW1wID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lSW1wX2ZvbGQyJGVuc2VtYmxfZ2VuZV9pZCkKCk5lY3JvID0gcmVhZC5kZWxpbSgiS0VHR05lY3JvcHRvc2lzX2hzYTA0MjE3XzA2LTI1LTE4LnR4dCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzID0gRikKTmVjcm8gPSBOZWNyb1tyb3dTdW1zKGlzLm5hKE5lY3JvKSkgPT0gMCwgXQpERV9OZWNybyA9IG1lcmdlKEdFX0ltcCwgTmVjcm8sIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX05lY3JvKSA9IG1ha2UubmFtZXMoREVfTmVjcm9bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChERV9OZWNyb1szOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFKQojIHdyaXRlLnRhYmxlKERFX05lY3JvLCAiS0VHR05lY3JvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCgpBcG9wID0gcmVhZC5kZWxpbSgiS0VHR0Fwb3B0b3Npc19oc2EwNDIxMF8wNi0yNS0xOC50eHQiLCBoZWFkZXI9VCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCkFwb3AgPSBBcG9wW3Jvd1N1bXMoaXMubmEoQXBvcCkpID09IDAsIF0KREVfQXBvcCA9IG1lcmdlKEdFX0ltcCksIEFwb3AsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0Fwb3ApID0gbWFrZS5uYW1lcyhERV9BcG9wWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfQXBvcFszOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFLCBzY2FsZSA9ICJyb3ciKQojIHdyaXRlLnRhYmxlKERFX0Fwb3AsICJLRUdHQXBvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCkZlcnIgPSByZWFkLmRlbGltKCJLRUdHRmVycm9wdG9zaXNfaHNhMDQyMTZfMDYtMjUtMTgudHh0IiwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpGZXJyID0gRmVycltyb3dTdW1zKGlzLm5hKEZlcnIpKSA9PSAwLCBdCkRFX0ZlcnIgPSBtZXJnZShHRV9JbXAsIEZlcnIsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0ZlcnIpID0gbWFrZS5uYW1lcyhERV9GZXJyWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfRmVycls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCiMgd3JpdGUudGFibGUoREVfRmVyciwgIktFR0dGZXJyb3B0b3NpcyBTQzEsNywxMCBERVNlcSBMUlQudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UpCgoKYGBgCgojIyMjIDQuIERpZmZlcmVudCBMQyBjb21wYXJpc29ucy4gQmV0d2VlbiBzdWJjbG9uZXMgYW5kIGF0IGJhc2VsaW5lIHZzIGlkbGluZy4KWnNjb3JlIGhlYXRtYXBzIG9mIHJlbGV2YW50IGNvbXBhcmlzb25zIGNhbiBiZSBtYWRlIGFzIGluIGFib3ZlIHRvIHZpc3VhbGl6ZS4KCmBgYHtyIGV2YWw9RkFMU0V9CgojVVNFUyBBQk9WRSBDT0RFIFRPIExJTkUgMjgwLiBSdW4gdGhhdCBwc2V1ZG8tZnVuY3Rpb24uCgojIGxpYnJhcnkocGhlYXRtYXApCiNDb21wYXJpc29ucyBvZiBkaWZFeCBiZXR3ZWVuIHN1YmNsb25lcyBhdCBiYXNlbGluZSBhbmQgaWRsaW5nCkJldHdlZW5CYXNlID0gYygiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiKQpCZXR3ZWVuSWRsZSA9IGMoIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IikKIAoKI1Vuc3VyZSBvZiBob3cgc3RyaWN0IHRvIG1ha2UgdGhlIGN1dG9mZi4gU2hvdWxkIGFsbCB0aGUgZ2VuZXMgYmV0d2VlbiBjbG9uZXMgYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkICgzKSBvciBpcyBhIHNpbmdsZSBkaWZmZXJlbmNlIHN1ZmZpY2llbnQ/CkJ0d19iID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBCZXR3ZWVuQmFzZSldCkJ0d19iMSA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MSxdCkJ0d19iMiA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MixdCkJ0d19iMyA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MyxdCgpCdHdfaSA9IEdFX2ZvbGRbLGMoImVuc2VtYmxfZ2VuZV9pZCIsICJoZ25jX3N5bWJvbCIsICJwYWRqIiwgQmV0d2VlbklkbGUpXQpCdHdfaTEgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTEsXQpCdHdfaTIgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTIsXQpCdHdfaTMgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTMsXQoKI1RoaXMgZG9lcyBub3QgYWNjb3VudCBmb3Igc2FtZSBkaXJlY3Rpb24gb2YgY2hhbmdlLiBUaGlzIGNhbiBiZSBwbG90dGVkIGluIGEgaGVhdG1hcCB0byB2aWV3LgojTWVtYmVycyB0aGF0IHdlcmUgImxvc3QiIGJ5IHRoZSBiYXNlbGluZSBjb25kaXRpb24gYXQgYmVpbmcgZGlmZmVyZW50LiBXZXJlIG5vIGxvbmdlciBmb3VuZCBkaWZmRXggYmV0d2VlbiB0aGUgc3ViY2xvbmVzIHdoZW4gY29tcGFyaW5nIGJhc2VsaW5lIHRvIGlkbGluZyBERUdzLgpMb3N0REVHX2JfMSA9IHN1YnNldChCdHdfYjEsIUJ0d19iMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kxJGVuc2VtYmxfZ2VuZV9pZCkKTG9zdERFR19iXzIgPSBzdWJzZXQoQnR3X2IyLCFCdHdfYjIkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMiRlbnNlbWJsX2dlbmVfaWQpCkxvc3RERUdfYl8zID0gc3Vic2V0KEJ0d19iMywgIUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjTWFrZSBoZWF0bWFwCkxvc3RERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMyRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfM19tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8zX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKI01lbWJlcnMgdGhhdCByZW1haW5lZCBkaWZmZXJlbnQuIApTdGF0aWNERUdfYl8xID0gc3Vic2V0KEJ0d19iMSxCdHdfYjEkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQpClN0YXRpY0RFR19iXzIgPSBzdWJzZXQoQnR3X2IyLEJ0d19iMiRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCkKU3RhdGljREVHX2JfMyA9IHN1YnNldChCdHdfYjMsIEJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjU29tZSBIR05DX3N5bWJvbHMgYXJlIGR1cGxpY2F0ZXMhIEkgc3dpdGNoZWQgdG8gZW5zZW1ibF9nZW5lX2lkIHRvIGZpeC4KU3RhdGljREVHX2lfMyA9IHN1YnNldChCdHdfaTMsIEJ0d19pMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IzJGVuc2VtYmxfZ2VuZV9pZCkKCgojI01ha2UgaGVhdG1hcApTdGF0aWNERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVTdGF0aWNERUdfYl8zJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhbikgPSBtYWtlLm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKFN0YXRpY0RFR19iXzNfbWVhbls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgoKI01lbWJlcnMgdGhhdCAiZ2FpbmVkIiBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBzdWJjbG9uZXMgaW4gaWRsaW5nLiAgCkdhaW5ERUdfaV8xID0gc3Vic2V0KEJ0d19pMSwgIUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IxJGVuc2VtYmxfZ2VuZV9pZCkKR2FpbkRFR19pXzIgPSBzdWJzZXQoQnR3X2kyLCAhQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCVpbiVCdHdfYjIkZW5zZW1ibF9nZW5lX2lkKQpHYWluREVHX2lfMyA9IHN1YnNldChCdHdfaTMsICFCdHdfaTMkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQpCgojI01ha2UgaGVhdG1hcApHYWluREVHX2lfM19tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lR2FpbkRFR19pXzMkZW5zZW1ibF9nZW5lX2lkKQpyb3cubmFtZXMoR2FpbkRFR19pXzNfbWVhbikgPSBtYWtlLm5hbWVzKEdhaW5ERUdfaV8zX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChHYWluREVHX2lfM19tZWFuWzQ6MTJdLGNsdXN0ZXJfY29scz1GQUxTRSwgc2NhbGUgPSAicm93IikKCgojTWVtYmVycyB0aGF0IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gaWRsaW5nICg4ZGF5KSBhbmQgYmFzZWxpbmUgd2l0aGluIHN1YmNsb25lcy4gVGhvc2Ugd2l0aCBzaGFyZWQgZGlmZkV4IG1heSBiZSBjb252ZXJnZW50IGFjcm9zcyBtdWx0aXBsZSBzdWJjbG9uZXMgZGVwZW5kaW5nIG9uIGRpcmVjdGlvbiBvZiBleHByZXNpc29uIGNoYW5nZS4KRW5kcG9pbnQgPSBjKCJTQzAxX2RheTgvU0MwMV9kYXkwIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAiU0MxMF9kYXk4L1NDMTBfZGF5MCIpCkJ0b0lkbGUgPSBHRV9mb2xkWyxjKCJlbnNlbWJsX2dlbmVfaWQiLCAiaGduY19zeW1ib2wiLCAicGFkaiIsIEVuZHBvaW50KV0KQnRvSWRsZV8xID0gQnRvSWRsZVtyb3dTdW1zKGFicyhCdG9JZGxlWyw0OjZdKT49MS41KT49MSxdCkJ0b0lkbGVfMiA9IEJ0b0lkbGVbcm93U3VtcyhhYnMoQnRvSWRsZVssNDo2XSk+PTEuNSk+PTIsXQpCdG9JZGxlXzMgPSBCdG9JZGxlW3Jvd1N1bXMoYWJzKEJ0b0lkbGVbLDQ6Nl0pPj0xLjUpPj0zLF0KCiMjTWFrZSBoZWF0bWFwCkJ0b0lkbGVfMl9tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lQnRvSWRsZV8yJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuKSA9IG1ha2UubmFtZXMoQnRvSWRsZV8yX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQoKQnRvSWRsZV8yX21lYW5faW5jRXhwID0gQnRvSWRsZV8yX21lYW5bd2hpY2goQnRvSWRsZV8yX21lYW4kU0MwMV9kYXkwIDwgQnRvSWRsZV8yX21lYW4kU0MwMV9kYXk4KSxdCkJ0b0lkbGVfMl9tZWFuX2luY0V4cCA9IEJ0b0lkbGVfMl9tZWFuX2luY0V4cFt3aGljaChCdG9JZGxlXzJfbWVhbl9pbmNFeHAkU0MwN19kYXkwIDwgQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMDdfZGF5OCksXQpCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2goQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMTBfZGF5MCA8IEJ0b0lkbGVfMl9tZWFuX2luY0V4cCRTQzEwX2RheTgpLF0KCkxvc3RERUdfYl8yX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMiRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfMl9tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzJfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8yX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKQnRvSWRsZUluY0V4cF9ERWJldHdlZW5TQ3MgPSBCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2gocm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuX2luY0V4cCkgJWluJSByb3cubmFtZXMoTG9zdERFR19iXzJfbWVhbikpLF0KCnBoZWF0bWFwKEJ0b0lkbGVfMl9tZWFuX2luY0V4cFs0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgojIGxpYnJhcnkoZGV2dG9vbHMpCiMgIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKIyBsaWJyYXJ5KGVucmljaFIpCmRicyA8LSBsaXN0RW5yaWNockRicygpCmhlYWQoZGJzKQpkYnMgPC0gYygiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiLCAiR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiLCAiR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiKQoKZW5yaWNoZWQgPC0gZW5yaWNocihyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSwgZGJzKQoKVmlldyhlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dKQpWaWV3KGVucmljaGVkW1siR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiXV0pClZpZXcoZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSkKCmVucmljaGVkX01GX3NpZyA8LSBlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dW2VucmljaGVkW1siR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiXV0kQWRqdXN0ZWQuUC52YWx1ZTwwLjA1LF0KZW5yaWNoZWRfTUZfc2lnX2RmIDwtIGRhdGEuZnJhbWUoZW5yaWNoZWRfTUZfc2lnWyxjKDEsNCw5KV0pCndyaXRlLmNzdihlbnJpY2hlZF9NRl9zaWdfZGYsICJlbnJpY2hlZF9NRl9zaWduaWZpY2FudC5jc3YiKQoKZW5yaWNoZWRfQlBfc2lnIDwtIGVucmljaGVkW1siR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiXV1bZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSRBZGp1c3RlZC5QLnZhbHVlPDAuMDUsXQplbnJpY2hlZF9CUF9zaWdfZGYgPC0gZGF0YS5mcmFtZShlbnJpY2hlZF9CUF9zaWdbLGMoMSw0LDkpXSkKIyB3cml0ZS5jc3YoZW5yaWNoZWRfQlBfc2lnX2RmLCAiZW5yaWNoZWRfQlBfc2lnbmlmaWNhbnQuY3N2IikKCkdpbmlfc2NHZW5lcyA8LSBjKCJBUE9FIiwgIkJDQU4iLCAiQ0VTMSIsICJDSVRFRDEiLAogICAgICAgICAgICAgICAgICAiQ1BNIiwgIkNUU0YiLCAiRENUIiwgIkVETlJCIiwgCiAgICAgICAgICAgICAgICAgICJFR1IxIiwgIkVTUlAxIiwgIkZTVEwxIiwgIk1BTEFUMSIsCiAgICAgICAgICAgICAgICAgICJNQVAySzYiLCAiTUNGMkwiLCAiTUxBTkEiLCAiTVhENCIsCiAgICAgICAgICAgICAgICAgICJPQ0EyIiwgIlBNRUwiLCAiU0VNQTZBIiwgIlNOQUkyIiwKICAgICAgICAgICAgICAgICAgIlNPWDQiLCAiVFNQQU4xMCIpCmVucmljaGVkX3NjIDwtIGVucmljaHIoR2luaV9zY0dlbmVzLCBkYnMpCgpyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSAlaW4lIEdpbmlfc2NHZW5lcwoKYGBgCgoKCiMjIyMgUmVzdCBvZiBKYWNrJ3MgQW5hbHlzaXMgIyMjIwpgYGB7ciBldmFsPUZBTFNFfQojVmlzdWFsbHkgaW5zcGVjdCB0cmVuZGluZyBtZW1iZXJzIGZyb20gaGVhdG1hcHMuCiNQbG90cyBvZiBzcGVjaWZpYyB0cmVuZGluZyBtZW1iZXJzPwpwIDwtIGdncGxvdChkYXRhPWRmMiwgYWVzKHg9ZG9zZSwgeT1sZW4sIGZpbGw9c3VwcCkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGNvbG9yPSJibGFjayIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBOT1RFOiBjb2RlIGJlbG93IHJldXNlcyBvYmplY3QgbmFtZXMuLi4gV0lMTCBPVkVSV1JJVEUhCmBgYHtyIGV2YWw9RkFMU0V9CiNHTE0gQ29lZiBIZWF0bWFwLgpiZXRhcyA8LSBjb2VmKGRkcykKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCm1hdCA8LSBkYXRhLmZyYW1lKGJldGFzW3RvcEdlbmVzLF0pCm1hdCRlbnNlbWJsX2dlbmVfaWQgPSByb3cubmFtZXMobWF0KQptYXQkcGFkaiA9IHJlc1t0b3BHZW5lcywicGFkaiJdCiMgZW5zZW1ibCA8LSB1c2VNYXJ0KCJlbnNlbWJsIikKIyBtYXJ0IDwtIHVzZURhdGFzZXQoImhzYXBpZW5zX2dlbmVfZW5zZW1ibCIsIG1hcnQgPSBlbnNlbWJsKQojIGdlbmVzID0gcm93Lm5hbWVzKG1hdCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKIyBHRV9kYXRhIDwtIG1lcmdlKG1hdCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQojIHJvd25hbWVzKEdFX2RhdGEpIDwtIG1ha2UubmFtZXMoR0VfZGF0YVssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCiMgR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI1NvcnRpbmcgc2NyaXB0IHRvIHBpY2sgb3V0IGVudHJpZXMgZ3JlYXRlciB0aGFuIG9yIGxlc3MgdGhhbiArLTEKZWcgPSBjKCkKZm9yKGkgaW4gMzoxMCl7CiAgZyA9IHdoaWNoKEdFX2RhdGFbLGldID4gMyB8IEdFX2RhdGFbLGldIDwgLTMpCiAgZWcgPSBjKGVnLGcpCn0KZWcgPSB1bmlxdWUoZWcpCgptYXQgPSBHRV9kYXRhW2VnLC1jKDE6MiwxMSwxMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKIyBsaWJyYXJ5KHBoZWF0bWFwKQpwaGVhdG1hcChtYXQsIGNsdXN0ZXJfY29scyA9IEZBTFNFKQoKIyBzc2RnID0gc2RnWzE6MTAwMCwgXQpkaW0oc2RnKQpoZWFkKHNkZykKCmBgYAoKCg==